<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/16/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/16/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/16/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/Redis-%E5%93%A8%E5%85%B5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/Redis-%E5%93%A8%E5%85%B5/" class="post-title-link" itemprop="url">Redis-哨兵</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-09 20:43:29" itemprop="dateCreated datePublished" datetime="2021-12-09T20:43:29+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-12 15:18:32" itemprop="dateModified" datetime="2021-12-12T15:18:32+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>Redis主从复制的作用有数据热备、负载均衡、故障恢复等；但主从复制存在的一个问题是故障恢复无法自动化。本文将要介绍的哨兵，它基于Redis主从复制，主要作用便是解决主节点故障恢复的自动化问题，进一步提高系统的高可用性。</p>
</blockquote>
<h2 id="部署哨兵节点"><a href="#部署哨兵节点" class="headerlink" title="部署哨兵节点"></a>部署哨兵节点</h2><p><strong>哨兵节点本质上是特殊的Redis节点</strong>，它的两个基本功能是<strong>监控</strong>和<strong>故障转移</strong>。</p>
<p>新建一个配置文件<code>sentinel.conf</code>,内容为</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">sentinel monitor mymaster ip port quorum</span></span><br><span class="line">sentinel monitor mymaster 192.168.92.128 6379 2</span><br></pre></td></tr></table></figure>



<p>其中，sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是：</p>
<p>该哨兵节点监控192.168.92.128:6379这个主节点，该主节点的名称是mymaster，最后的2表示最低通过票数，与主节点的故障判定有关：至少需要2个哨兵节点同意，才能判定主节点故障并进行故障转移。</p>
<p>注意，在配置哨兵监控一个系统的时候，只需要配置其监控主数据库即可，哨兵会自动发现所有复制该主库的从库。</p>
<p>哨兵节点的启动有两种方式，二者作用是完全相同的：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">redis-sentinel sentinel-26379.conf</span><br><span class="line">redis-server sentinel-26379.conf --sentinel</span><br></pre></td></tr></table></figure>


<p>哨兵系统的搭建过程，有几点需要注意：</p>
<p>（1）哨兵系统中的主从节点，与普通的主从节点并没有什么区别，故障发现和转移是由哨兵来控制和完成的。</p>
<p>（2）哨兵节点本质上是redis节点。</p>
<p>（3）<strong>每个哨兵节点，只需要配置监控主节点</strong>，便可以自动发现其他的哨兵节点和从节点。</p>
<p>（4）在哨兵节点启动和故障转移阶段，各个节点的配置文件会被重写(config rewrite)。</p>
<h2 id="Jedis使用哨兵"><a href="#Jedis使用哨兵" class="headerlink" title="Jedis使用哨兵"></a>Jedis使用哨兵</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">testSentinel</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">         <span class="type">String</span> <span class="variable">masterName</span> <span class="operator">=</span> <span class="string">&quot;mymaster&quot;</span>;</span><br><span class="line">         Set&lt;String&gt; sentinels = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">         sentinels.add(<span class="string">&quot;192.168.92.128:26379&quot;</span>);</span><br><span class="line">         sentinels.add(<span class="string">&quot;192.168.92.128:26380&quot;</span>);</span><br><span class="line">         sentinels.add(<span class="string">&quot;192.168.92.128:26381&quot;</span>);</span><br><span class="line"> </span><br><span class="line">         <span class="type">JedisSentinelPool</span> <span class="variable">pool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JedisSentinelPool</span>(masterName, sentinels); <span class="comment">//初始化过程做了很多工作</span></span><br><span class="line">         <span class="type">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> pool.getResource();</span><br><span class="line">         jedis.set(<span class="string">&quot;key1&quot;</span>, <span class="string">&quot;value1&quot;</span>);</span><br><span class="line">         pool.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>Jedis客户端对哨兵提供了很好的支持。如上述代码所示，我们只需要向Jedis提供哨兵节点集合和masterName，构造JedisSentinelPool对象；然后便可以像使用普通redis连接池一样来使用了：通过pool.getResource()获取连接，执行具体的命令。</p>
<p>在整个过程中，我们的代码<strong>不需要显式的指定主节点的地址</strong>，就可以连接到主节点；</p>
<p>代码中对故障转移没有任何体现，就可以<strong>在哨兵完成故障转移后自动的切换主节点</strong>。</p>
<p>之所以可以做到这一点，是因为在JedisSentinelPool的构造器中，进行了相关的工作；主要包括以下两点：</p>
<p>（1）<strong>遍历哨兵节点，获取主节点信息：</strong>遍历哨兵节点，通过其中一个哨兵节点+masterName获得主节点的信息；该功能是通过调用哨兵节点的sentinel get-master-addr-by-name命令实现，该命令示例如下：</p>
<p>一旦获得主节点信息，停止遍历（因此一般来说遍历到第一个哨兵节点，循环就停止了）。</p>
<p>（2）<strong>增加对哨兵的监听：</strong>这样当<strong>发生故障转移时，客户端便可以收到哨兵的通知</strong>，从而完成主节点的切换。具体做法是：<strong>利用redis提供的发布订阅功能，为每一个哨兵节点开启一个单独的线程，订阅哨兵节点的+switch-master频道，当收到消息时，重新初始化连接池</strong>。</p>
<p><strong>需要注意的是，哨兵只是配置提供者，而不是代理</strong>。</p>
<h2 id="哨兵的原理"><a href="#哨兵的原理" class="headerlink" title="哨兵的原理"></a>哨兵的原理</h2><p>关于哨兵的原理，关键是了解以下几个概念。</p>
<h3 id="建立连接"><a href="#建立连接" class="headerlink" title="建立连接"></a>建立连接</h3><h3 id="定时任务"><a href="#定时任务" class="headerlink" title="定时任务"></a>定时任务</h3><p>每个哨兵节点维护了3个定时任务。定时任务的功能分别如下：1. 通过向主从节点发送info命令获取最新的主从结构；2. 通过发布订阅功能获取其他哨兵节点的信息；3. 通过向其他节点发送ping命令进行心跳检测，判断是否下线。</p>
<ol>
<li><p><strong>每10秒哨兵会向主数据库和从数据库发送Info命令</strong>。发送Info命令是的哨兵能够获得主节点的相关信息，并能够获得到从节点的信息，从而<strong>实现对新节点的自动发现</strong>。</p>
</li>
<li><p><strong>每2秒哨兵向主数据库和从数据库的__sentinel__:hello频道发送自己的信息</strong> ，用于分享和获取同样监控此数据库的哨兵节点的信息。当其他节点受到信息后，会判断发消息的哨兵节点是否是新发现的节点，如果是则会加入哨兵列表。</p>
</li>
<li><p><strong>每30秒会向主数据库、从数据库和其他哨兵节点发送PING命令</strong>。此命令的作用就是用于<strong>定时监控这些数据库和哨兵节点有没有停止服务</strong>，很关键的一点。</p>
</li>
</ol>
<h3 id="主观下线和客观下线"><a href="#主观下线和客观下线" class="headerlink" title="主观下线和客观下线"></a>主观下线和客观下线</h3><ul>
<li><p>主观下线：在心跳检测的定时任务中，如果其他节点超过一定时间没有回复，哨兵节点就会将其进行主观下线。顾名思义，<strong>主观下线的意</strong>思是一个哨兵节点“主观地”判断下线，也就是哨兵它自己认为一个节点下线了，但还没有得到其他节点的认同；与主观下线相对应的是客观下线。</p>
</li>
<li><p>客观下线：哨兵节点在对主节点进行主观下线后，会通过<code>sentinel is-master-down-by-addr</code>命令询问其他哨兵节点该主节点的状态；<strong>如果判断主节点下线的哨兵数量达到一定数值，则对该主节点进行客观下线</strong>。</p>
</li>
</ul>
<p>需要特别注意的是，<strong>客观下线是主节点才有的概念</strong>；<strong>如果从节点和哨兵节点发生故障，被哨兵主观下线后，不会再有后续的客观下线和故障转移操作</strong>。</p>
<h3 id="选举领导者哨兵节点"><a href="#选举领导者哨兵节点" class="headerlink" title="选举领导者哨兵节点"></a>选举领导者哨兵节点</h3><p>当主节点被判断客观下线以后，各个哨兵节点会进行协商，选举出一个领导者哨兵节点，并由该领导者节点对其进行故障转移操作。</p>
<p>监视该主节点的所有哨兵都有可能被选为领导者，选举使用的算法是Raft算法。</p>
<p>使用Raft算法选举领头哨兵的过程如下：</p>
<ol>
<li>发现主数据库客观下线的哨兵节点（称为节点A），向每个哨兵节点发送命令，要求对方选自己称为领导者哨兵。</li>
<li>如果目标哨兵节点没有选过其他人，则会同意将A设置成领头节点。</li>
<li>如果A发现有超过半数且超过quorum参数值的哨兵节点同意选自己为领导者节点，则A成功成为领导者节点。</li>
<li>当有多个哨兵节点同时参选领头节点的时候，则会出现任何节点都不能当选的情况，这个时候每个参选节点都等待一个随机时间重新发起参选请求，进行下一轮选举，直到选举成功。</li>
</ol>
<p><strong>注意：每次选举出来的最多只会选出一个领头哨兵。</strong></p>
<h3 id="故障恢复"><a href="#故障恢复" class="headerlink" title="故障恢复"></a>故障恢复</h3><p>选举出的领导者哨兵，开始进行故障恢复操作，该操作大体可以分为3个步骤： </p>
<ol>
<li><strong>在从节点中选择新的主节点</strong>：选择的原则是，<strong>首先过滤掉不健康的从节点；然后选择优先级最高的从节点(由slave-priority指定)；如果优先级无法区分，则选择复制偏移量最大的从节点；如果仍无法区分，则选择runid最小的从节点</strong>。（健康状态–&gt;优先级–&gt;复制偏移量–&gt;runid）</li>
<li><strong>更新主从状态</strong>：<strong>通过slaveof no one命令，让选出来的从节点成为主节点</strong>；并<strong>通过slaveof命令让其他节点成为其从节点</strong>。</li>
<li><strong>将已经下线的主节点(如localhost:6379)设置为新的主节点的从节点</strong>，当localhost:6379重新上线后，它会成为新的主节点的从节点。</li>
</ol>
<p>通过上述几个关键概念，可以基本了解哨兵的工作原理。为了更形象的说明，下图展示了领导者哨兵节点的日志，包括从节点启动到完成故障转移。</p>
<p><img src="/2021/12/09/Redis-%E5%93%A8%E5%85%B5/Redis-%E5%93%A8%E5%85%B51.png"></p>
<h2 id="配置与实践建议"><a href="#配置与实践建议" class="headerlink" title="配置与实践建议"></a>配置与实践建议</h2><h3 id="参数配置"><a href="#参数配置" class="headerlink" title="参数配置"></a>参数配置</h3><p>下面介绍与哨兵相关的几个配置。</p>
<ol>
<li>sentinel monitor {masterName} {masterIp} {masterPort} {quorum}</li>
</ol>
<p>sentinel monitor是哨兵最核心的配置，在前文讲述部署哨兵节点时已说明，其中：masterName指定了主节点名称，masterIp和masterPort指定了主节点地址<strong>，quorum是判断主节点客观下线的哨兵数量阈值</strong>：当判定主节点下线的哨兵数量达到quorum时，对主节点进行客观下线。<strong>quorum建议取值为N&#x2F;2 + 1（其中 N 为哨兵节点的数量）</strong>。</p>
<ol start="2">
<li>sentinel down-after-milliseconds {masterName} {time}</li>
</ol>
<p>sentinel down-after-milliseconds与主观下线的判断有关：<strong>哨兵使用ping命令对其他节点进行心跳检测，如果其他节点超过down-after-milliseconds配置的时间没有回复，哨兵就会将其进行主观下线</strong>。该配置对主节点、从节点和哨兵节点的主观下线判定都有效。</p>
<p>down-after-milliseconds的默认值是30000，即30s；可以根据不同的网络环境和应用要求来调整：值越大，对主观下线的判定会越宽松，好处是误判的可能性小，坏处是故障发现和故障转移的时间变长，客户端等待的时间也会变长。例如，如果应用对可用性要求较高，则可以将值适当调小，当故障发生时尽快完成转移；如果网络环境相对较差，可以适当提高该阈值，避免频繁误判。</p>
<ol start="3">
<li>sentinel parallel-syncs {masterName} {number}</li>
</ol>
<p>sentinel parallel-syncs与故障转移之后从节点的复制有关，<strong>规定了每次向新的主节点发起复制操作的从节点个数</strong>。例如，假设主节点切换完成之后，有3个从节点要向新的主节点发起复制；如果parallel-syncs&#x3D;1，则从节点会一个一个开始复制；如果parallel-syncs&#x3D;3，则3个从节点会一起开始复制。</p>
<p>parallel-syncs取值越大，从节点完成复制的时间越快，但是对主节点的网络负载、硬盘负载造成的压力也越大；应根据实际情况设置。例如，如果主节点的负载较低，而从节点对服务可用的要求较高，可以适量增加parallel-syncs取值。parallel-syncs的默认值是1。</p>
<ol start="4">
<li>sentinel failover-timeout {masterName} {time}</li>
</ol>
<p>sentinel failover-timeout与故障转移超时的判断有关，但是<strong>该参数不是用来判断整个故障恢复阶段的超时</strong>，而是其几个子阶段的超时，例如如果<strong>从节点晋升为主节点时间超过timeout</strong>，或<strong>从节点向新的主节点发起复制操作的时间</strong>(不包括复制数据的时间)超过timeout，都会导致故障转移超时失败。</p>
<p>failover-timeout的默认值是180000，即180s；如果超时，则下一次该值会变为原来的2倍。</p>
<h3 id="实践建议"><a href="#实践建议" class="headerlink" title="实践建议"></a>实践建议</h3><p>（1）<strong>哨兵节点的数量应不止一个</strong>，一方面增加哨兵节点的冗余，避免哨兵本身成为高可用的瓶颈；另一方面减少对下线的误判。此外，<strong>这些不同的哨兵节点应部署在不同的物理机上</strong>。</p>
<p>（2）<strong>哨兵节点的数量应该是奇数</strong>，便于哨兵通过投票做出“决策”：领导者选举的决策、客观下线的决策等。</p>
<p>（3）<strong>各个哨兵节点的配置应一致</strong>，包括硬件、参数等；此外，所有节点都应该使用ntp或类似服务，保证时间准确、一致。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/undolog/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/undolog/" class="post-title-link" itemprop="url">MySQL —— Undo log</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-09 18:47:10" itemprop="dateCreated datePublished" datetime="2021-12-09T18:47:10+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-11 01:53:31" itemprop="dateModified" datetime="2021-12-11T01:53:31+08:00">2021-12-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MySQL/" itemprop="url" rel="index"><span itemprop="name">MySQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="undo-log的作用"><a href="#undo-log的作用" class="headerlink" title="undo log的作用"></a>undo log的作用</h2><p>为了保证事务的原子性，InnoDB引入了undo日志，undo日志记载了回滚操作所需的内容。</p>
<p>undo是<strong>逻辑日志</strong>，只是将数据库逻辑地恢复到原来的样子；所有修改都被逻辑地取消了，但是数据结构和页本身在回滚之后可能不大相同。</p>
<p>undo log有两个作用：<strong>提供回滚</strong>和**多版本并发控制(MVCC)**。</p>
<h2 id="事务id"><a href="#事务id" class="headerlink" title="事务id"></a>事务id</h2><p>在事务在对表中的记录进行修改的时候，会为这个事务分配一个唯一的事务id，这个事务id是递增的，所以事务id越小越先执行。未被分配事务id的事务的id默认是0。</p>
<h2 id="各种类型的Undo-log"><a href="#各种类型的Undo-log" class="headerlink" title="各种类型的Undo log"></a>各种类型的Undo log</h2><p>InnoDB对不同场景的设计了不同类型的undo日志，比如TRX_UNDO_INSERT_REC, TRX_UNDO_DEL_MARK_REC.</p>
<p>TRX_UNDO_UPD_EXIST_REC等等。</p>
<p>除了insert产生的undo日志中没有roll_pointer字段，其他的一般都有roll_pointer这个字段，这个字段就支撑了回滚和MVCC的功能。</p>
<p><img src="/2021/12/09/undolog/undolog.png"></p>
<h2 id="Undo-log-的管理"><a href="#Undo-log-的管理" class="headerlink" title="Undo log 的管理"></a>Undo log 的管理</h2><p>类型为FIL_PAGE_UNDO_LOG的页面，是专门用来存储undo日志的，不妨称为Undo页面。</p>
<p>一个事务写的undo日志可能在一个页面中放不下，所以MySQL使用链表来组织这些undo页面。</p>
<p>在一个事务执行过程中，最多分配4个页面链表，</p>
<p>分别是</p>
<ol>
<li>针对普通表的insert undo 链表；</li>
<li>针对普通表的update undo 链表；</li>
<li>针对临时表的insert undo 链表；</li>
<li>针对临时表的update undo 链表；</li>
</ol>
<p>每个undo页面链表都对应一个Undo Log Segment。Undo页面链表的第一个页中有一个名为Undo Log Segment Header的部分，用来存储关于这个段的信息。</p>
<p>同一个事务向一个undo链表写入的undo日志算是一个组，每个组都以一个Undo Log Header 部分开头。</p>
<h2 id="undo及redo如何记录事务"><a href="#undo及redo如何记录事务" class="headerlink" title="undo及redo如何记录事务"></a>undo及redo如何记录事务</h2><h3 id="Undo-Redo事务的简化过程"><a href="#Undo-Redo事务的简化过程" class="headerlink" title="Undo + Redo事务的简化过程"></a>Undo + Redo事务的简化过程</h3><p>假设有A、B两个数据，值分别为1,2，开始一个事务，事务的操作内容为：把1修改为3，2修改为4，那么实际的记录如下（简化）：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">A.事务开始.</span><br><span class="line">B.记录A=1到undo log.</span><br><span class="line">C.修改A=3.</span><br><span class="line">D.记录A=3到redo log.</span><br><span class="line">E.记录B=2到undo log.</span><br><span class="line">F.修改B=4.</span><br><span class="line">G.记录B=4到redo log.</span><br><span class="line">H.将redo log写入磁盘。</span><br><span class="line">I.事务提交</span><br></pre></td></tr></table></figure>

<p><strong>注意是先写 undo log，再操作，最后再写redo log</strong></p>
<h3 id="崩溃恢复"><a href="#崩溃恢复" class="headerlink" title="崩溃恢复"></a>崩溃恢复</h3><p>前面说到未提交的事务和回滚了的事务也会记录Redo Log，因此在进行恢复时,这些事务要进行特殊的的处理。有2种不同的恢复策略：</p>
<ul>
<li>进行恢复时，只重做已经提交了的事务。</li>
<li>进行恢复时，<strong>重做所有事务包括未提交的事务和回滚了的事务，然后通过Undo Log回滚那些未提交的事务</strong>。</li>
</ul>
<p>MySQL数据库InnoDB存储引擎使用了B策略, InnoDB存储引擎中的恢复机制有几个特点：</p>
<ul>
<li><p><strong>在重做Redo Log时，并不关心事务性</strong>。 恢复时，没有BEGIN，也没有COMMIT,ROLLBACK的行为。也不关心每个日志是哪个事务的。尽管事务ID等事务相关的内容会记入Redo Log，这些内容只是被当作要操作的数据的一部分。</p>
</li>
<li><p>使用B策略就<strong>必须要将Undo Log持久化</strong>，而且<strong>必须要在写Redo Log之前将对应的Undo Log写入磁盘</strong>。Undo和Redo Log的这种关联，使得持久化变得复杂起来。</p>
<p>为了降低复杂度，<strong>InnoDB将Undo Log看作数据，因此记录Undo Log的操作也会记录到redo log中</strong>。</p>
<p><strong>这样undo log就可以像数据一样缓存起来</strong>，<strong>而不用在redo log之前写入磁盘了</strong>。</p>
<p>包含Undo Log操作的Redo Log，看起来是这样的：</p>
</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">记录1: &lt;trx1, Undo log insert &lt;undo_insert …&gt;&gt; # 记录的 undo-log</span><br><span class="line">记录2: &lt;trx1, insert …&gt; # 重做日志本身</span><br><span class="line">记录3: &lt;trx2, Undo log insert &lt;undo_update …&gt;&gt;</span><br><span class="line">记录4: &lt;trx2, update …&gt;</span><br><span class="line">记录5: &lt;trx3, Undo log insert &lt;undo_delete …&gt;&gt;</span><br><span class="line">记录6: &lt;trx3, delete …&gt;</span><br></pre></td></tr></table></figure>



<p>到这里，还有一个问题没有弄清楚。既然Redo没有事务性，那岂不是会重新执行被回滚了的事务？<br>确实是这样。同时Innodb也会将事务回滚时的操作也记录到redo log中。<strong>回滚操作本质上也是对数据进行修改，因此回滚时对数据的操作也会记录到Redo Log中</strong>。</p>
<p><strong>一个回滚了的事务的Redo Log</strong>，看起来是这样的：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">记录1: &lt;trx1, Undo log insert &lt;undo_insert …&gt;&gt; # 记录的 undo-log</span><br><span class="line">记录2: &lt;trx1, insert A…&gt; # 重做日志本身</span><br><span class="line">记录3: &lt;trx1, Undo log insert &lt;undo_update …&gt;&gt;</span><br><span class="line">记录4: &lt;trx1, update B…&gt;</span><br><span class="line">记录5: &lt;trx1, Undo log insert &lt;undo_delete …&gt;&gt;</span><br><span class="line">记录6: &lt;trx1, delete C…&gt;</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下面是对应的回滚操作</span></span><br><span class="line">记录7: &lt;trx1, insert C&gt; </span><br><span class="line">记录8: &lt;trx1, update B to old value&gt;</span><br><span class="line">记录9: &lt;trx1, delete A&gt;</span><br></pre></td></tr></table></figure>


<p><strong>一个被回滚了的事务在恢复时的操作就是先redo再undo，因此不会破坏数据的一致性</strong>。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/redolog/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/redolog/" class="post-title-link" itemprop="url">MySQL —— Redo log</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-09 18:47:05" itemprop="dateCreated datePublished" datetime="2021-12-09T18:47:05+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-11 01:05:41" itemprop="dateModified" datetime="2021-12-11T01:05:41+08:00">2021-12-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MySQL/" itemprop="url" rel="index"><span itemprop="name">MySQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="RedoLog介绍"><a href="#RedoLog介绍" class="headerlink" title="RedoLog介绍"></a>RedoLog介绍</h1><h2 id="引言——MySQL如何做到事务的持久性"><a href="#引言——MySQL如何做到事务的持久性" class="headerlink" title="引言——MySQL如何做到事务的持久性"></a>引言——MySQL如何做到事务的持久性</h2><p>MySQL中如何修改数据呢，先从磁盘中加载数据页到InnoDB buffer pool中，然后从InnoDB buffer pool 中取出页面，进行读写操作。</p>
<p>那么如何做到事务的持久性呢，也就是说如何确保InnoDB buffer pool中的脏页一定会被刷新到磁盘从而做到持久化呢？</p>
<p>一种最简单的方法，在事务提交成功之前，确保脏页被刷新到磁盘，换句话说，只有在脏页写到磁盘上之后，事务提交才算成功。</p>
<p>不过这种方案有缺点：</p>
<ol>
<li><strong>每次都刷新一个完整的页到磁盘性能开销很大</strong>。比如，一个页面中只有几个字节被修改的时候，刷新完整的页面到磁盘很浪费。</li>
<li><strong>磁盘的随机IO很慢</strong>，一个事务修改的很多页面可能并不相邻，这个时候每个事务结束前都这样同步刷新的磁盘随机IO代价很大。</li>
</ol>
<p>MySQL采取的做法是，仅仅记录下那些修改的操作，譬如<strong>修改某个表空间的某个页面的某个偏移量的某几个字节</strong>，然后将这样的记录及时写到文件中。然后每次事务提交前只需要刷新这样的日志文件到磁盘即可。</p>
<p>而这样的日志文件就是 <strong>rodo log</strong> 了。</p>
<p>刷新redo-log而不是完整的数据页到磁盘的好处有：</p>
<ol>
<li>redo日志相对来讲比较紧凑，占用的空间比较小；</li>
<li>redo日志是顺序写入磁盘的，磁盘的顺序IO效率很高。</li>
</ol>
<h2 id="redo日志格式"><a href="#redo日志格式" class="headerlink" title="redo日志格式"></a>redo日志格式</h2><p>redo log 包括两部分：</p>
<ol>
<li><p>一个是内存中的日志缓冲(redo log buffer)；</p>
</li>
<li><p>另一个是磁盘上的日志文件(redo log file)。</p>
</li>
</ol>
<p>下面依次介绍</p>
<ol>
<li>type：这条redo日志的格式；</li>
<li>space ID：表空间 ID；</li>
<li>page number：页号；</li>
<li>data：这条redo日志的具体内容。</li>
</ol>
<p>具体来说redo日志的种类有非常多，这里就不细谈了。</p>
<p>挖个坑。</p>
<h2 id="Mini-transaction-的概念"><a href="#Mini-transaction-的概念" class="headerlink" title="Mini-transaction 的概念"></a>Mini-transaction 的概念</h2><p>MySQL对底层页面的一次原子性的访问成为一个Mini-Transaction（MTR）</p>
<p>一个MTR产生多个redo日志。</p>
<p>之所以把它成为迷你的一个事务，是因为在进行崩溃恢复的时候需要把这一组redo日志作为一个不可分割的整体来处理。</p>
<img src="mtr.png" style="zoom:30%;" />



<h2 id="redo日志的管理"><a href="#redo日志的管理" class="headerlink" title="redo日志的管理"></a>redo日志的管理</h2><h3 id="log-block"><a href="#log-block" class="headerlink" title="log block"></a>log block</h3><p>redo日志放在大小为512字节的log block 中。每一个block被分为 3 部分：</p>
<ul>
<li>log block header</li>
<li>log block body</li>
<li>log block trailer</li>
</ul>
<p>而真正的redo日志是被放在占用 496 字节的 block body 中的。</p>
<h3 id="redo-log-buffer"><a href="#redo-log-buffer" class="headerlink" title="redo log buffer"></a>redo log buffer</h3><p>为了解决磁盘速度过慢的问题，写入redo日志也不是直接立即写到磁盘中的。</p>
<p>于是，MySQL向操作系统申请了一大片内存，称为redo 日志缓冲区，也成为 redo buffer，</p>
<p>一个redo buffer有若干的 log block 构成。可以指定<code>innodb_log_buffer_size</code>来指定 log buffer 的大小。</p>
<p>向 log buffer 中写入redo日志也是顺序写入的。</p>
<h2 id="redo-log的刷盘时机"><a href="#redo-log的刷盘时机" class="headerlink" title="redo log的刷盘时机"></a>redo log的刷盘时机</h2><p>redo日志总留在log buffer中，总是不安全的，只要没有写到稳定的存储介质（磁盘、固定硬盘等等）中就不能保证持久性。</p>
<p>那么在什么情况下，redo日志会被写到磁盘上呢？</p>
<ol>
<li><p>事务提交时：道理很好理解，事务提交还没有将redo log 刷盘就无法做到持久性；</p>
</li>
<li><p>log buffer 空间不足时：log buffer 这块内存总是有限大小的，那么在redo log不够使用时，也需要即使把redo log 刷新到磁盘。InnoDB在日志量达到总容量的额50% 时，就会刷盘；</p>
</li>
<li><p>后台线程定时刷新；</p>
</li>
<li><p>正常关闭服务器时；</p>
</li>
<li><p>做checkpoint时。</p>
</li>
</ol>
<h2 id="redo日志文件"><a href="#redo日志文件" class="headerlink" title="redo日志文件"></a>redo日志文件</h2><p>之前谈的所有的redo log其实都是内存性的结构，那么存在在稳定介质中的便是 redo日志文件了。</p>
<p>磁盘上的日志文件不止一个，而是以一个日志文件组的形式出现，一个日志文件组由多个日志文件组成，名字形式为”ib_logfile[数字]”。<br>每个日志文件的大小由<code>innodb_log_file_size</code>指定，默认是 48 MB。<br>日志文件组文件的个数由<code>innodb_log_files_in_group</code>指定，默认是2个。</p>
<p>所以，总体的日志文件的大小便是<strong>innodb_log_file_size*innodb_log_files_in_group</strong>，</p>
<p>如果以循环的方法向redo日志文件中写入数据，那岂不是新写入的会覆盖旧写入的吗？</p>
<p>如何解决这个问题，MySQL提出了 checkpoint的概念（后面或介绍）。</p>
<p>每个日志的文件格式是一样的，日志组内的其他的日志文件也是具有相同的格式。</p>
<p>前2048个字节存储一些管理信息，后面的字节，实际上就是对内存中的log buffer的镜像。</p>
<h2 id="log-sequence-number相关"><a href="#log-sequence-number相关" class="headerlink" title="log-sequence-number相关"></a>log-sequence-number相关</h2><h3 id="lsn"><a href="#lsn" class="headerlink" title="lsn"></a>lsn</h3><p>InndoDB设计了名为lsn的全局变量，用来记录当前已经写入的<strong>lsn</strong>值，初始值为8704，然后不断递增。</p>
<p>lsn指的是写入的redo 日志量，这包括了写到了 log buffer 中但没有刷新到磁盘的redo日志。</p>
<h3 id="flushed-to-disk-lsn"><a href="#flushed-to-disk-lsn" class="headerlink" title="flushed_to_disk_lsn"></a>flushed_to_disk_lsn</h3><p>相应的，InndoDB设计者提出了一个表示刷新到磁盘中的redo日志量的全局变量<strong>flushed_to_disk_lsn</strong>。</p>
<p>一开始，lsn等于flushed_to_disk_lsn，随着系统的运行，redo日志不断写入log buffer，但又没有即使刷盘，那么flushed_to_disk_lsn便和lsn拉开了差距。</p>
<h3 id="checkpoint-lsn"><a href="#checkpoint-lsn" class="headerlink" title="checkpoint_lsn"></a>checkpoint_lsn</h3><p>redo日志的大小总是有限的，那么就有必要对其进行重复利用。</p>
<p>具体的，如果redo日志对应的脏页已经刷新到磁盘，那么此时就没有必要再去保留这样的redo日志，</p>
<p>于是InnodB使用<strong>checkpoint_lsn</strong>表示当前系统中可以被覆盖redo日志总量是多少，这个变量的初始值也是8704。</p>
<p>MySQL中有后台线程在脏页刷新到磁盘的时候执行一次checkpoint操作，就是查看是否可以增加checkpoint_lsn（但是不是说只要刷新脏页就一定执行一次checkpoint操作）。</p>
<h2 id="innodb-flush-log-at-trx-commit"><a href="#innodb-flush-log-at-trx-commit" class="headerlink" title="innodb_flush_log_at_trx_commit"></a>innodb_flush_log_at_trx_commit</h2><p>它的取值有3种：</p>
<ul>
<li>0 ：事务提交时不会立即向磁盘同步redo日志。这样会加快处理速度，但是失去持久性这一保证，不可取。</li>
<li>1 ：事务提交时必须将redo日志同步到磁盘，这样可以保证事务的持久性。默认值就是1。</li>
<li>2：这是一个折中的选择，事务提交的时候需要同步到操作系统的写磁盘的缓冲区，但不一定要真正写到磁盘上。所以，如果数据库挂了，但是操作系统还没挂，持久性还是可以保证的；反之，持久性还是无法保证。</li>
</ul>
<h1 id="崩溃恢复"><a href="#崩溃恢复" class="headerlink" title="崩溃恢复"></a>崩溃恢复</h1><p>redo日志最重要的甚至是唯一的功能就是在数据库崩溃的时候提供数据恢复的保证了。</p>
<p>崩溃恢复的时候先找到恢复的起点。然后顺着日志文件就可以将数据正确恢复起来。</p>
<p>但是这个过程可以加快。</p>
<p>为了避免页面的随机IO，可以实现将redo文件的所有redo文件按照<strong>spaceID和pageNumber</strong><br>为键，然后组织成一张哈希表，然后遍历这张hash表就可以一次性完成对一个页面的恢复。<br>当然，同一个页面的redo日志必须安装时间顺序排序。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/Raft/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/Raft/" class="post-title-link" itemprop="url">Raft</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-09 13:58:09" itemprop="dateCreated datePublished" datetime="2021-12-09T13:58:09+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-29 04:13:13" itemprop="dateModified" datetime="2022-03-29T04:13:13+08:00">2022-03-29</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>参考</p>
<p><a target="_blank" rel="noopener" href="https://zinglix.xyz/2020/06/25/raft/">https://zinglix.xyz/2020/06/25/raft/</a></p>
<p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ZmRS9Gjh9P-4lPLZ9jWk_A">https://mp.weixin.qq.com/s/ZmRS9Gjh9P-4lPLZ9jWk_A</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/Redis-Info%E5%91%BD%E4%BB%A4%E8%BE%93%E5%87%BA%E8%AF%A6%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/Redis-Info%E5%91%BD%E4%BB%A4%E8%BE%93%E5%87%BA%E8%AF%A6%E8%A7%A3/" class="post-title-link" itemprop="url">Redis-Info命令输出详解</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-12-09 13:34:10 / 修改时间：13:59:00" itemprop="dateCreated datePublished" datetime="2021-12-09T13:34:10+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Redis-Info-输出详解"><a href="#Redis-Info-输出详解" class="headerlink" title="Redis Info 输出详解"></a>Redis Info 输出详解</h1><p>Redis 的 info 命令是使用频率很高的一个命令，它主要是显示 Redis 服务器当前状态，故我们经常用于监控Redis 服务器。我收集了一些参数解释，方便随时查询</p>
<h1 id="Info命令简介"><a href="#Info命令简介" class="headerlink" title="Info命令简介"></a>Info命令简介</h1><p>在使用Redis的过程中，可能会遇到很多问题，需要我们去诊断、去观察Redis的健康情况。Redis给我们提供了的 <code>info</code> 命令，可以让我们近距离的接触它，观察它各方面的信息、运行状况。下面让我们看看 <code>info</code> 命令都给我们带来了哪些信息。</p>
<p>命令格式：<code>INFO [section]</code></p>
<p>Info 指令显示的信息分为 9 大块，每块都有很多参数。我们也可以通过给定可选的参数 section ，可以让命令只返回某一部分的信息。这 9 块分别是:</p>
<ul>
<li><code>server</code> 部分记录了 Redis 服务器的信息</li>
<li><code>clients</code> 部分记录了已连接客户端的信息</li>
<li><code>memory</code> 部分记录了服务器的内存信息</li>
<li><code>persistence</code> 部分记录了跟 RDB 持久化和 AOF 持久化有关的信息</li>
<li><code>stats</code> 部分记录了一般统计信息</li>
<li><code>replication</code> 部分记录了主&#x2F;从复制的相关信息</li>
<li><code>cpu</code> 部分记录了 CPU 的计算量统计信息</li>
<li><code>cluster</code> 部分记录了和集群有关的信息</li>
<li><code>keyspace</code> 部分记录了数据库相关的统计信息</li>
</ul>
<blockquote>
<p>不同版本的 Redis 可能对返回的一些域进行了增加或删减。 因此，一个健壮的客户端程序在对 INFO [section] 命令的输出进行分析时，应该能够跳过不认识的域，并且妥善地处理丢失不见的域。</p>
</blockquote>
<h2 id=""><a href="#" class="headerlink" title=""></a></h2><h2 id="Server"><a href="#Server" class="headerlink" title="Server"></a>Server</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>redis_version</td>
<td>Redis 的服务器版本</td>
</tr>
<tr>
<td>redis_git_sha1</td>
<td>Redis 的服务器版本</td>
</tr>
<tr>
<td>redis_git_dirty</td>
<td>Git dirty flag</td>
</tr>
<tr>
<td>redis_build_id</td>
<td></td>
</tr>
<tr>
<td>redis_mode</td>
<td>运行模式：单机（集群）</td>
</tr>
<tr>
<td>os</td>
<td>Redis 服务器的宿主操作系统</td>
</tr>
<tr>
<td>arch_bits</td>
<td>架构（32 或 64 位）</td>
</tr>
<tr>
<td>multiplexing_api</td>
<td>Redis 所使用的事件处理机制，如epoll</td>
</tr>
<tr>
<td>gcc_version</td>
<td>编译 Redis 时所使用的 GCC 版本</td>
</tr>
<tr>
<td>process_id</td>
<td>服务器进程的 PID</td>
</tr>
<tr>
<td>run_id</td>
<td>Redis 服务器的随机标识符（用于 Sentinel 和集群）</td>
</tr>
<tr>
<td>tcp_port</td>
<td>TCP&#x2F;IP 监听端口</td>
</tr>
<tr>
<td>uptime_in_seconds</td>
<td>自 Redis 服务器启动以来，经过的秒数</td>
</tr>
<tr>
<td>uptime_in_days</td>
<td>自 Redis 服务器启动以来，经过的天数</td>
</tr>
<tr>
<td>hz</td>
<td>redis内部调度（进行关闭timeout的客户端，删除过期key等等）频率，程序规定serverCron每秒运行10次。</td>
</tr>
<tr>
<td>lru_clock</td>
<td>以分钟为单位进行自增的时钟，用于 LRU 管理</td>
</tr>
<tr>
<td>executable</td>
<td>启动脚本路径</td>
</tr>
<tr>
<td>config_file</td>
<td>启动时指定的配置文件（redis.conf）路径</td>
</tr>
</tbody></table>
<h2 id="Clients"><a href="#Clients" class="headerlink" title="Clients"></a>Clients</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>connected_clients</td>
<td>已连接客户端的数量（不包括通过从属服务器连接的客户端）</td>
</tr>
<tr>
<td>client_longest_output_list</td>
<td>当前连接的客户端当中，最长的输出列表</td>
</tr>
<tr>
<td>client_longest_input_buf</td>
<td>当前连接的客户端当中，最大输入缓存</td>
</tr>
<tr>
<td>blocked_clients</td>
<td>正在等待阻塞命令（BLPOP、BRPOP、BRPOPLPUSH）的客户端的数量</td>
</tr>
</tbody></table>
<h2 id="Memory"><a href="#Memory" class="headerlink" title="Memory"></a>Memory</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>used_memory</td>
<td>使用内存（B）</td>
</tr>
<tr>
<td>used_memory_human</td>
<td>人类可读的格式的使用内存（MB）</td>
</tr>
<tr>
<td>used_memory_rss</td>
<td>操作系统角度，返回redis已分配的内存（即常驻内存），这个值和top、ps命令的输出一致</td>
</tr>
<tr>
<td>used_memory_rss_human</td>
<td>如上</td>
</tr>
<tr>
<td>used_memory_peak</td>
<td>内存使用的峰值</td>
</tr>
<tr>
<td>used_memory_peak_human</td>
<td>如上</td>
</tr>
<tr>
<td>total_system_memory</td>
<td>整个系统内存</td>
</tr>
<tr>
<td>total_system_memory_human</td>
<td>如上</td>
</tr>
<tr>
<td>used_memory_lua</td>
<td>Lua脚本存储占用的内存</td>
</tr>
<tr>
<td>used_memory_lua_human</td>
<td>如上</td>
</tr>
<tr>
<td>maxmemory</td>
<td>Redis实例的最大内存配置</td>
</tr>
<tr>
<td>maxmemory_human</td>
<td>如上</td>
</tr>
<tr>
<td>maxmemory_policy</td>
<td>当达到maxmemory时的淘汰策略</td>
</tr>
<tr>
<td>mem_fragmentation_ratio</td>
<td>used_memory_rss&#x2F;used_memory的值。一般情况下，used_memory_rss略高于used_memory，当内存碎片较多时，则mem_fragmentation_ratio会较大，可以反映内存碎片是否很多</td>
</tr>
<tr>
<td>mem_allocator</td>
<td>内存分配器。可以是libc 、 jemalloc 或者 tcmalloc</td>
</tr>
</tbody></table>
<p>在理想情况下， <code>used_memory_rss</code> 的值应该只比 <code>used_memory</code> 稍微高一点儿。 当 <code>rss &gt; used</code> ，且两者的值相差较大时，表示存在（内部或外部的）内存碎片。 内存碎片的比率可以通过 <code>mem_fragmentation_ratio</code> 的值看出。 当 <code>used &gt; rss</code> 时，表示 Redis 的部分内存被操作系统换出到交换空间（swap）了，在这种情况下，操作可能会产生明显的延迟，需要重点关注。即：<code>mem_fragmentation_ratio</code> 小于1时</p>
<h2 id="Persistence"><a href="#Persistence" class="headerlink" title="Persistence"></a>Persistence</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>loading</td>
<td>服务器是否正在载入持久化文件</td>
</tr>
<tr>
<td>rdb_changes_since_last_save</td>
<td>离最近一次成功生成rdb文件，写入命令的个数</td>
</tr>
<tr>
<td>rdb_bgsave_in_progress</td>
<td>服务器是否正在创建rdb文件</td>
</tr>
<tr>
<td>rdb_last_save_time</td>
<td>最近一次成功rdb文件的时间戳</td>
</tr>
<tr>
<td>rdb_last_bgsave_status</td>
<td>最近一次成功rdb文件的状态</td>
</tr>
<tr>
<td>rdb_last_bgsave_time_sec</td>
<td>最近一次成功rdb文件的耗时</td>
</tr>
<tr>
<td>rdb_current_bgsave_time_sec</td>
<td>若当前正在创建rdb文件，指当前的创建操作已经耗费的时间</td>
</tr>
<tr>
<td>aof_enabled</td>
<td>aof是否开启</td>
</tr>
<tr>
<td>aof_rewrite_in_progress</td>
<td>aof的rewrite操作是否在进行中</td>
</tr>
<tr>
<td>aof_rewrite_scheduled</td>
<td>rewrite任务计划，当客户端发送bgrewriteaof指令，如果当前rewrite子进程正在执行，那么将客户端请求的bgrewriteaof变为计划任务，待aof子进程结束后执行rewrite</td>
</tr>
<tr>
<td>aof_last_rewrite_time_sec</td>
<td>最近一次aof rewrite耗费时长</td>
</tr>
<tr>
<td>aof_current_rewrite_time_sec</td>
<td>若当前正在执行aof rewrite，指当前的已经耗费的时间</td>
</tr>
<tr>
<td>aof_last_bgrewrite_status</td>
<td>最近一次aof bgrewrite的状态</td>
</tr>
<tr>
<td>aof_last_write_status</td>
<td>最近一次aof写入状态</td>
</tr>
<tr>
<td>开启 aof 后增加的一些info信息</td>
<td></td>
</tr>
<tr>
<td>aof_current_size</td>
<td>aof文件当前大小</td>
</tr>
<tr>
<td>aof_base_size</td>
<td>服务器启动时或者最近一次AOF重写后，文件的大小</td>
</tr>
<tr>
<td>aof_pending_rewrite</td>
<td>同上面的aof_rewrite_scheduled</td>
</tr>
<tr>
<td>aof_buffer_length</td>
<td>aof 缓冲区的大小</td>
</tr>
<tr>
<td>aof_rewrite_buffer_length</td>
<td>aof 重写缓冲区的大小</td>
</tr>
<tr>
<td>aof_pending_bio_fsync</td>
<td>后台IO队列中，等待fsync任务的个数</td>
</tr>
<tr>
<td>aof_delayed_fsync</td>
<td>被延迟的 fsync 调用数量</td>
</tr>
</tbody></table>
<h2 id="Stats"><a href="#Stats" class="headerlink" title="Stats"></a>Stats</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>total_connections_received</td>
<td>自启动起连接过的总数。如果连接过多，说明短连接严重或连接池使用有问题，需调研代码的连接设置</td>
</tr>
<tr>
<td>total_commands_processed</td>
<td>自启动起运行命令的总数</td>
</tr>
<tr>
<td>instantaneous_ops_per_sec</td>
<td>每秒执行的命令数，相当于QPS</td>
</tr>
<tr>
<td>total_net_input_bytes</td>
<td>网络入口流量字节数</td>
</tr>
<tr>
<td>total_net_output_bytes</td>
<td>网络出口流量字节数</td>
</tr>
<tr>
<td>instantaneous_input_kbps</td>
<td>网络入口kps</td>
</tr>
<tr>
<td>instantaneous_output_kbps</td>
<td>网络出口kps</td>
</tr>
<tr>
<td>rejected_connections</td>
<td>拒绝的连接个数，由于maxclients限制，拒绝新连接的个数</td>
</tr>
<tr>
<td>sync_full</td>
<td>主从完全同步成功次数</td>
</tr>
<tr>
<td>sync_partial_ok</td>
<td>主从部分同步成功次数</td>
</tr>
<tr>
<td>sync_partial_err</td>
<td>主从部分同步失败次数</td>
</tr>
<tr>
<td>expired_keys</td>
<td>自启动起过期的key的总数</td>
</tr>
<tr>
<td>evicted_keys</td>
<td>使用内存大于maxmemory后，淘汰的key的总数</td>
</tr>
<tr>
<td>keyspace_hits</td>
<td>在main dictionary字典中成功查到的key个数</td>
</tr>
<tr>
<td>keyspace_misses</td>
<td>同上，未命中的key的个数</td>
</tr>
<tr>
<td>pubsub_channels</td>
<td>发布&#x2F;订阅频道数</td>
</tr>
<tr>
<td>pubsub_patterns</td>
<td>发布&#x2F;订阅模式数</td>
</tr>
<tr>
<td>latest_fork_usec</td>
<td>上次的fork操作使用的时间（单位ms）</td>
</tr>
<tr>
<td>migrate_cached_sockets</td>
<td>是否已经缓存了到该地址的连接</td>
</tr>
<tr>
<td>slave_expires_tracked_keys</td>
<td>从实例到期key数量</td>
</tr>
<tr>
<td>active_defrag_hits</td>
<td>主动碎片整理命中次数</td>
</tr>
<tr>
<td>active_defrag_misses</td>
<td>主动碎片整理未命中次数</td>
</tr>
<tr>
<td>active_defrag_key_hits</td>
<td>主动碎片整理key命中次数</td>
</tr>
<tr>
<td>active_defrag_key_misses</td>
<td>主动碎片整理key未命中次数</td>
</tr>
</tbody></table>
<h2 id="Replication"><a href="#Replication" class="headerlink" title="Replication"></a>Replication</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>role</td>
<td>当前实例的角色master还是slave</td>
</tr>
<tr>
<td>connected_slaves:</td>
<td>slave的数量</td>
</tr>
<tr>
<td>master_replid</td>
<td>主实例启动随机字符串</td>
</tr>
<tr>
<td>master_replid2</td>
<td>主实例启动随机字符串2</td>
</tr>
<tr>
<td>slave0</td>
<td>slave机器的信息、状态</td>
</tr>
<tr>
<td>master_repl_offset</td>
<td>主从同步偏移量,此值如果和上面的offset相同说明主从一致没延迟，与master_replid可被用来标识主实例复制流中的位置。</td>
</tr>
<tr>
<td>second_repl_offset</td>
<td>主从同步偏移量2,此值如果和上面的offset相同说明主从一致没延迟</td>
</tr>
<tr>
<td>repl_backlog_active</td>
<td>复制缓冲区是否开启</td>
</tr>
<tr>
<td>repl_backlog_size</td>
<td>复制缓冲区大小</td>
</tr>
<tr>
<td>repl_backlog_first_byte_offset</td>
<td>复制缓冲区里偏移量的大小</td>
</tr>
<tr>
<td>repl_backlog_histlen</td>
<td>此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小</td>
</tr>
</tbody></table>
<h2 id="CPU"><a href="#CPU" class="headerlink" title="CPU"></a>CPU</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>used_cpu_sys</td>
<td>Redis 服务器耗费的系统 CPU</td>
</tr>
<tr>
<td>used_cpu_user</td>
<td>Redis 服务器耗费的用户 CPU</td>
</tr>
<tr>
<td>used_cpu_sys_children</td>
<td>后台进程耗费的系统 CPU</td>
</tr>
<tr>
<td>used_cpu_user_children</td>
<td>后台进程耗费的用户 CPU</td>
</tr>
</tbody></table>
<h2 id="Keyspace"><a href="#Keyspace" class="headerlink" title="Keyspace"></a>Keyspace</h2><table>
<thead>
<tr>
<th>参数名称</th>
<th>参数含义</th>
</tr>
</thead>
<tbody><tr>
<td>dbXXX:keys&#x3D;XXX,expires&#x3D;XXX</td>
<td>各个数据库（0-15）的 key 的数量，带有生存期的 key 的数量，平均存活时间</td>
</tr>
</tbody></table>
<p><strong>实例</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Server</span></span><br><span class="line">redis_version:3.2.3					 # Redis 的版本</span><br><span class="line">redis_git_sha1:00000000				 # Redis 的版本</span><br><span class="line">redis_git_dirty:0</span><br><span class="line">redis_build_id:9e93d0c7997bcfef</span><br><span class="line">redis_mode:standalone				 # 运行模式：单机（集群）</span><br><span class="line">os:Linux 2.6.32-431.el6.x86_64 x86_64 # 操作系统</span><br><span class="line">arch_bits:64						  # 操作系统位数</span><br><span class="line">multiplexing_api:epoll				 # redis所使用的事件处理机制</span><br><span class="line">gcc_version:4.4.7					 # gcc版本号</span><br><span class="line">process_id:1606						 # 当前 Redis 服务器进程id</span><br><span class="line">run_id:17e79b1966f1f891eff203a8e496151ee8a3a7a7</span><br><span class="line">tcp_port:7001						 # 端口号</span><br><span class="line">uptime_in_seconds:4360189			 # 运行时间(秒)</span><br><span class="line">uptime_in_days:50					 # 运行时间(天)</span><br><span class="line">hz:10								 # redis内部调度（进行关闭timeout的客户端，删除过期key等等）频率，程序规定serverCron每秒运行10次。</span><br><span class="line">lru_clock:5070330					 # Redis的逻辑时钟</span><br><span class="line">executable:/usr/local/bin/redis-server			# 启动脚本路径</span><br><span class="line">config_file:/opt/redis3/conf/redis_7001.conf	# 启动指定的配置文件路径</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Clients</span></span><br><span class="line">connected_clients:660				 # 连接的客户端数量</span><br><span class="line">client_longest_output_list:0		 # 当前连接的客户端当中，最长的输出列表</span><br><span class="line">client_biggest_input_buf:0			 # 当前连接的客户端当中，最大输入缓存</span><br><span class="line">blocked_clients:0					 # 阻塞的客户端数量</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Memory</span></span><br><span class="line">used_memory:945408832				# 使用内存（B）</span><br><span class="line">used_memory_human:901.61M			# 使用内存（MB）	</span><br><span class="line">used_memory_rss:1148919808			# 系统给redis分配的内存（即常驻内存），这个值和top命令的输出一致</span><br><span class="line">used_memory_rss_human:1.07G</span><br><span class="line">used_memory_peak:1162079480			# 内存使用的峰值</span><br><span class="line">used_memory_peak_human:1.08G		</span><br><span class="line">total_system_memory:6136483840		# 整个系统内存</span><br><span class="line">total_system_memory_human:5.72G</span><br><span class="line">used_memory_lua:122880				# Lua脚本存储占用的内存</span><br><span class="line">used_memory_lua_human:120.00K		</span><br><span class="line">maxmemory:2147483648				# Redis实例的最大内存配置</span><br><span class="line">maxmemory_human:2.00G</span><br><span class="line">maxmemory_policy:allkeys-lru		# 当达到maxmemory时的淘汰策略</span><br><span class="line">mem_fragmentation_ratio:1.22		# used_memory_rss/used_memory的比例。一般情况下，used_memory_rss略高于used_memory，当内存碎片较多时，则mem_fragmentation_ratio会较大，可以反映内存碎片是否很多</span><br><span class="line">mem_allocator:jemalloc-4.0.3		# 内存分配器</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Persistence</span>	</span><br><span class="line">loading:0								  # 服务器是否正在载入持久化文件</span><br><span class="line">rdb_changes_since_last_save:82423954	  #	离最近一次成功生成rdb文件，写入命令的个数                      </span><br><span class="line">rdb_bgsave_in_progress:0		          # 服务器是否正在创建rdb文件           </span><br><span class="line">rdb_last_save_time:1560991229		      # 最近一次成功rdb文件的时间戳               </span><br><span class="line">rdb_last_bgsave_status:ok		          # 最近一次成功rdb文件的状态           </span><br><span class="line">rdb_last_bgsave_time_sec:-1		          # 最近一次成功rdb文件的耗时            </span><br><span class="line">rdb_current_bgsave_time_sec:-1		      # 若当前正在创建rdb文件，指当前的创建操作已经耗费的时间                </span><br><span class="line">aof_enabled:0		                      # aof是否开启</span><br><span class="line">aof_rewrite_in_progress:0		          # aof的rewrite操作是否在进行中            </span><br><span class="line">aof_rewrite_scheduled:0		              # rewrite任务计划，当客户端发送bgrewriteaof指令，如果当前rewrite子进程正在执行，那么将客户端请求的bgrewriteaof变为计划任务，待aof子进程结束后执行rewrite        </span><br><span class="line">aof_last_rewrite_time_sec:-1		      # 最近一次aof rewrite耗费时长              </span><br><span class="line">aof_current_rewrite_time_sec:-1		      # 若当前正在执行aof rewrite，指当前的已经耗费的时间                </span><br><span class="line">aof_last_bgrewrite_status:ok		      # 最近一次aof bgrewrite的状态         </span><br><span class="line">aof_last_write_status:ok		          # 最近一次aof写入状态  </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">开启aof后增加的一些info信息</span></span><br><span class="line">-----------------------------  </span><br><span class="line">aof_current_size:0                 # aof当前大小</span><br><span class="line">aof_base_size:0                    # aof上次启动或rewrite的大小</span><br><span class="line">aof_pending_rewrite:0              # 同上面的aof_rewrite_scheduled</span><br><span class="line">aof_buffer_length:0                # aof buffer的大小</span><br><span class="line">aof_rewrite_buffer_length:0        # aof rewrite buffer的大小</span><br><span class="line">aof_pending_bio_fsync:0            # 后台IO队列中等待fsync任务的个数</span><br><span class="line">aof_delayed_fsync:0                # 延迟的fsync计数器 </span><br><span class="line">-----------------------------           </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Stats</span></span><br><span class="line">total_connections_received:15815		# 自启动起连接过的总数。如果连接过多，说明短连接严重或连接池使用有问题，需调研代码的连接设置</span><br><span class="line">total_commands_processed:502953838      # 自启动起运行命令的总数</span><br><span class="line">instantaneous_ops_per_sec:7             # 每秒执行的命令数，相当于QPS</span><br><span class="line">total_net_input_bytes:532510481889      # 网络入口流量字节数</span><br><span class="line">total_net_output_bytes:1571444057940    # 网络出口流量字节数</span><br><span class="line">instantaneous_input_kbps:0.37           # 网络入口kps</span><br><span class="line">instantaneous_output_kbps:0.59          # 网络出口kps</span><br><span class="line">rejected_connections:0                  # 拒绝的连接个数，由于maxclients限制，拒绝新连接的个数</span><br><span class="line">sync_full:1                             # 主从完全同步成功次数</span><br><span class="line">sync_partial_ok:0                       # 主从部分同步成功次数</span><br><span class="line">sync_partial_err:0                      # 主从部分同步失败次数</span><br><span class="line">expired_keys:4404930                    # 自启动起过期的key的总数</span><br><span class="line">evicted_keys:0                          # 使用内存大于maxmemory后，淘汰的key的总数</span><br><span class="line">keyspace_hits:337104556                 # 在main dictionary字典中成功查到的key个数</span><br><span class="line">keyspace_misses:22865229                # 同上，未命中的key的个数</span><br><span class="line">pubsub_channels:1                       # 发布/订阅频道数</span><br><span class="line">pubsub_patterns:0                       # 发布/订阅模式数</span><br><span class="line">latest_fork_usec:707                    # 上次的fork操作使用的时间（单位ms）</span><br><span class="line">migrate_cached_sockets:0                # 是否已经缓存了到该地址的连接</span><br><span class="line">slave_expires_tracked_keys:0			# 从实例到期key数量</span><br><span class="line">active_defrag_hits:0                    # 主动碎片整理命中次数</span><br><span class="line">active_defrag_misses:0                  # 主动碎片整理未命中次数</span><br><span class="line">active_defrag_key_hits:0                # 主动碎片整理key命中次数</span><br><span class="line">active_defrag_key_misses:0              # 主动碎片整理key未命中次数</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Replication</span></span><br><span class="line">role:master							  # 当前实例的角色master还是slave</span><br><span class="line">connected_slaves:1					  # slave的数量</span><br><span class="line">master_replid:8f81c045a2cb00f16a7fc5c90a95e02127413bcc		# 主实例启动随机字符串</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000     # 主实例启动随机字符串2</span><br><span class="line">slave0:ip=172.17.12.251,port=7002,state=online,offset=506247209326,lag=1	# slave机器的信息、状态</span><br><span class="line">master_repl_offset:506247209478		  # 主从同步偏移量,此值如果和上面的offset相同说明主从一致没延迟，与master_replid可被用来标识主实例复制流中的位置。</span><br><span class="line">second_repl_offset					  # 主从同步偏移量2,此值如果和上面的offset相同说明主从一致没延迟</span><br><span class="line">repl_backlog_active:1				  # 复制缓冲区是否开启</span><br><span class="line">repl_backlog_size:157286400			  # 复制缓冲区大小</span><br><span class="line">repl_backlog_first_byte_offset:506089923079		# 复制缓冲区里偏移量的大小</span><br><span class="line">repl_backlog_histlen:157286400		  # 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">CPU</span></span><br><span class="line">used_cpu_sys:6834.06				  # 将所有redis主进程在核心态所占用的CPU时求和累计起来</span><br><span class="line">used_cpu_user:8282.10				  # 将所有redis主进程在用户态所占用的CPU时求和累计起来</span><br><span class="line">used_cpu_sys_children:0.11			  # 后台进程的核心态cpu使用率</span><br><span class="line">used_cpu_user_children:0.91           # 后台进程的用户态cpu使用率</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Cluster</span></span><br><span class="line">cluster_enabled:0		# 实例是否启用集群模式</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Keyspace		<span class="comment"># 各个数据库（0-15）的 key 的数量，带有生存期的 key 的数量，平均存活时间</span></span></span><br><span class="line">db0:keys=267906,expires=109608,avg_ttl=3426011859194</span><br><span class="line">db1:keys=182,expires=179,avg_ttl=503527626</span><br><span class="line">db8:keys=6,expires=0,avg_ttl=0</span><br><span class="line">db15:keys=2,expires=0,avg_ttl=0</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/Redis-%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/Redis-%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6/" class="post-title-link" itemprop="url">Redis-主从复制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-12-09 13:19:34 / 修改时间：14:56:32" itemprop="dateCreated datePublished" datetime="2021-12-09T13:19:34+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="主从复制"><a href="#主从复制" class="headerlink" title="主从复制"></a>主从复制</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>主从复制，是指将一台Redis服务器的数据，复制到其他的Redis服务器。前者称为主节点(master)，后者称为从节点(slave)；数据的复制是单向的，只能由主节点到从节点。</p>
<p>默认情况下，每台Redis服务器都是主节点；且一个主节点可以有多个从节点(或没有从节点)，但一个从节点只能有一个主节点。</p>
<p><strong>主从复制的作用</strong></p>
<p>主从复制的作用主要包括：</p>
<ol>
<li><p><strong>数据备份</strong>：主从复制实现了<strong>数据的热备份</strong>，是持久化之外的一种数据冗余方式。</p>
</li>
<li><p>故障恢复：当主节点出现问题时，可以由从节点提供服务，实现快速的故障恢复；实际上是一种服务的冗余。</p>
</li>
<li><p><strong>负载均衡</strong>：在主从复制的基础上，配合<strong>读写分离</strong>，可以<strong>由主节点提供写服务，由从节点提供读服务</strong>（即写Redis数据时应用连接主节点，读Redis数据时应用连接从节点），分担服务器负载；尤其是在写少读多的场景下，通过多个从节点分担读负载，可以大大提高Redis服务器的并发量。</p>
<p>默认情况下，从库只能读，不可写。</p>
</li>
<li><p>高可用基石：除了上述作用以外，<strong>主从复制还是哨兵和集群能够实施的基础</strong>，因此说主从复制是Redis高可用的基础。</p>
</li>
</ol>
<h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><ol>
<li><strong>建立复制</strong></li>
</ol>
<p>需要注意，主从复制的开启，完全是在从节点发起的；不需要我们在主节点做任何事情。</p>
<p>从节点开启主从复制，有3种方式：</p>
<p>（1）配置文件</p>
<p>在从服务器的配置文件中加入：slaveof masterip masterport</p>
<p>（2）启动命令</p>
<p>redis-server启动命令后加入 –slaveof masterip masterport</p>
<p>（3）客户端命令</p>
<p>Redis服务器启动后，直接通过客户端执行命令：slaveof masterip masterport，则该Redis实例成为从节点。</p>
<p>上述3种方式是等效的，下面以客户端命令的方式为例，看一下当执行了slaveof后，Redis主节点和从节点的变化。</p>
<ol start="2">
<li><strong>改变主库</strong></li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">slaveof masterip masterport</span><br></pre></td></tr></table></figure>



<ol start="3">
<li><strong>停止复制</strong></li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">slaveof no one</span><br></pre></td></tr></table></figure>










<h2 id="上手实战"><a href="#上手实战" class="headerlink" title="上手实战"></a>上手实战</h2><p><strong>启动一主二从的集群架构</strong></p>
<p>分别在3个shell窗口中</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis-server</span><br><span class="line">redis-server --port 6380 --slaveof 127.0.0.1 6379</span><br><span class="line">redis-server --port 6381 --slaveof 127.0.0.1 6379</span><br></pre></td></tr></table></figure>

<p>当然这里也可以在配置文件里修改然后指定配置文件启动。</p>
<p><strong>使用客户端连接</strong></p>
<p>分别在3个shell窗口中</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis-cli -p 6379</span><br><span class="line">redis-cli -p 6380</span><br><span class="line">redis-cli -p 6381</span><br></pre></td></tr></table></figure>





<p>使用<code>info replication</code>检查复制状态：</p>
<p><strong>主库状态</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; info replication</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Replication</span></span><br><span class="line">role:master # 角色：主库</span><br><span class="line">connected_slaves:2 # 从库数量</span><br><span class="line">slave0:ip=127.0.0.1,port=6380,state=online,offset=224,lag=0 # ip、端口、状态、复制偏移量</span><br><span class="line">slave1:ip=127.0.0.1,port=6381,state=online,offset=224,lag=1 </span><br><span class="line">master_failover_state:no-failover</span><br><span class="line">master_replid:4b6a1f35a960797cfce477656751eb3add4b320a</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:224</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:1  # 复制缓冲区</span><br><span class="line">repl_backlog_size:1048576 # 复制缓冲区大小（默认是1MB）</span><br><span class="line">repl_backlog_first_byte_offset:1</span><br><span class="line">repl_backlog_histlen:224</span><br></pre></td></tr></table></figure>





<p><strong>从库状态</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6380&gt; info replication</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Replication</span></span><br><span class="line">role:slave # 角色：从库</span><br><span class="line">master_host:127.0.0.1</span><br><span class="line">master_port:6379</span><br><span class="line">master_link_status:up</span><br><span class="line">master_last_io_seconds_ago:3</span><br><span class="line">master_sync_in_progress:0</span><br><span class="line">slave_repl_offset:238 # 从库偏移量</span><br><span class="line">slave_priority:100</span><br><span class="line">slave_read_only:1 # 从库只读（默认）</span><br><span class="line">replica_announced:1</span><br><span class="line">connected_slaves:0</span><br><span class="line">master_failover_state:no-failover</span><br><span class="line">master_replid:4b6a1f35a960797cfce477656751eb3add4b320a</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:238 # 主库 偏移量 (二者相同表示复制没有延迟)</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:1</span><br><span class="line">repl_backlog_size:1048576</span><br><span class="line">repl_backlog_first_byte_offset:1</span><br><span class="line">repl_backlog_histlen:238</span><br></pre></td></tr></table></figure>



<h2 id="主从复制的步骤"><a href="#主从复制的步骤" class="headerlink" title="主从复制的步骤"></a>主从复制的步骤</h2><h3 id="连接建立阶段"><a href="#连接建立阶段" class="headerlink" title="连接建立阶段"></a>连接建立阶段</h3><p>该阶段的主要作用是在主从节点之间建立连接，为数据同步做好准备。</p>
<ol>
<li><p><strong>保存主节点信息</strong><br>从节点服务器内部维护了两个字段，即masterhost和masterport字段，用于存储主节点的ip和port信息。需要注意的是，slaveof是异步命令，从节点完成主节点ip和port的保存后，向发送slaveof命令的客户端直接返回OK，实际的复制操作在这之后才开始进行。</p>
</li>
<li><p><strong>建立socket连接</strong><br>从节点每秒1次调用复制定时函数replicationCron()，如果发现了有主节点可以连接，便会根据主节点的ip和port，创建socket连接。如果连接成功，则：<strong>从节点为该socket建立一个专门处理复制工作的文件事件处理器，负责后续的复制工作，如接收RDB文件、接收命令传播等</strong>。<strong>主节点接收到从节点的socket连接后（即accept之后），为该socket创建相应的客户端状态，并将从节点看做是连接到主节点的一个客户端，后面的步骤会以从节点向主节点发送命令请求的形式来进行</strong>。</p>
</li>
<li><p><strong>发送ping命令检测</strong><br>从节点成为主节点的客户端之后，发送ping命令进行首次请求，<strong>目的是检查socket连接是否可用，以及主节点当前是否能够处理请求</strong>。从节点发送ping命令后，可能出现3种情况：</p>
<p>（1）返回pong：说明socket连接正常，且主节点当前可以处理请求，复制过程继续。</p>
<p>（2）超时：一定时间后从节点仍未收到主节点的回复，说明socket连接不可用，则从节点断开socket连接，并重连。</p>
<p>（3）返回pong以外的结果：如果主节点返回其他结果，如正在处理超时运行的脚本，说明主节点当前无法处理命令，则从节点断开socket连接，并重连。</p>
</li>
</ol>
<h3 id="数据同步阶段"><a href="#数据同步阶段" class="headerlink" title="数据同步阶段"></a>数据同步阶段</h3><p>主从节点之间的连接建立以后，便可以开始进行数据同步，该阶段可以理解为从节点数据的初始化。具体执行的方式是：从节点向主节点发送<code>psync</code>命令（Redis2.8以前是<code>sync</code>命令），开始同步。</p>
<p><strong>数据同步阶段是主从复制最核心的阶段</strong>，根据主从节点当前状态的不同，可以分为<strong>全量复制</strong>和<strong>增量复制</strong>，下面会有一章专门讲解这两种复制方式以及psync命令的执行过程，这里不再详述。</p>
<h3 id="命令传播阶段"><a href="#命令传播阶段" class="headerlink" title="命令传播阶段"></a>命令传播阶段</h3><p>数据同步阶段完成后，主从节点进入命令传播阶段；在这个阶段主节点将自己执行的写命令发送给从节点，从节点接收命令并执行，从而保证主从节点数据的一致性。</p>
<p>在命令传播阶段，除了发送写命令，主从节点还维持着<strong>心跳机制：PING和REPLCONF ACK</strong>。由于心跳机制的原理涉及增量复制，因此将在介绍了部分复制的相关内容后单独介绍该心跳机制。</p>
<p><strong>延迟与不一致</strong></p>
<p>需要注意的是，命令传播是异步的过程，即主节点发送写命令后并不会等待从节点的回复；因此实际上主从节点之间很难保持实时的一致性，延迟在所难免。</p>
<p>数据不一致的程度，与<strong>主从节点之间的网络状况</strong>、<strong>主节点写命令的执行频率</strong>、以及<strong>主节点中的repl-disable-tcp-nodelay配置</strong>等有关。</p>
<p><code>repl-disable-tcp-nodelay no</code>：该配置作用于命令传播阶段，控制主节点是否禁止与从节点的TCP_NODELAY；默认no，即不禁止TCP_NODELAY。当设置为yes时，TCP会对包进行合并从而减少带宽，但是发送的频率会降低，从节点数据延迟增加，一致性变差；具体发送频率与Linux内核的配置有关，默认配置为40ms。当设置为no时，TCP会立马将主节点的数据发送给从节点，带宽增加但延迟变小。</p>
<p>一般来说，只有当应用对Redis数据不一致的容忍度较高，且主从节点之间网络状况不好时，才会设置为yes；多数情况使用默认值no。</p>
<h2 id="数据同步阶段（全量复制和部分复制）"><a href="#数据同步阶段（全量复制和部分复制）" class="headerlink" title="数据同步阶段（全量复制和部分复制）"></a>数据同步阶段（全量复制和部分复制）</h2><p>在Redis2.8以前，从节点向主节点发送<code>sync</code>命令请求同步数据，此时的同步方式是全量复制；</p>
<p>在Redis2.8及以后，从节点可以发送<code>psync</code>命令请求同步数据，此时<strong>根据主从节点当前状态的不同</strong>，同步方式可能是全量复制或部分复制。后文介绍以Redis2.8及以后版本为例。</p>
<ol>
<li>全量复制：<strong>用于初次复制或其他无法进行部分复制的情况</strong>，<strong>将主节点中的所有数据都发送给从节点</strong>，是一个非常重量级的操作。</li>
<li>部分复制：<strong>用于网络中断等情况后的复制</strong>，<strong>只将中断期间主节点执行的写命令发送给从节点</strong>，与全量复制相比更加高效。需要注意的是，<strong>如果网络中断时间过长，导致主节点没有能够完整地保存中断期间执行的写命令，则无法进行部分复制，仍使用全量复制</strong>。</li>
</ol>
<h3 id="全量复制"><a href="#全量复制" class="headerlink" title="全量复制"></a>全量复制</h3><p>Redis通过<code>psync</code>命令进行全量复制的过程如下：</p>
<p>（1）从节点判断无法进行部分复制，向主节点发送全量复制的请求；或从节点发送部分复制的请求，但主节点判断无法进行部分复制；具体判断过程需要在讲述了部分复制原理后再介绍。</p>
<p>（2）主节点收到全量复制的命令后，执行bgsave，<strong>在后台生成RDB文件</strong>，<strong>并使用一个缓冲区（称为复制缓冲区）记录从现在开始执行的所有写命令。</strong></p>
<p>（3）主节点的bgsave执行完成后，<strong>将RDB文件发送给从节点</strong>；从节点首先清除自己的旧数据，然后载入接收的RDB文件，将数据库状态更新至主节点执行bgsave时的数据库状态。</p>
<p>（4）主节点<strong>将前述复制缓冲区中的所有写命令发送给从节点</strong>，从节点执行这些写命令，将数据库状态更新至主节点的最新状态</p>
<p>（5）如果<strong>从节点开启了AOF，则会触发bgrewriteaof的执行</strong>，从而保证AOF文件更新至主节点的最新状态。</p>
<p>通过全量复制的过程可以看出，全量复制是非常重型的操作：</p>
<p>（1）主节点通过bgsave命令fork子进程进行RDB持久化，该过程是非常消耗CPU、内存(页表复制)、硬盘IO的；</p>
<p>（2）主节点通过网络将RDB文件发送给从节点，对主从节点的带宽都会带来很大的消耗；</p>
<p>（3）<strong>从节点清空老数据、载入新RDB文件的过程是阻塞的</strong>，无法响应客户端的命令；如果从节点执行bgrewriteaof，也会带来额外的消耗。</p>
<blockquote>
<p>可以使用无硬盘复制，也就是说在复制初始化阶段，主节点不会把RDB写到磁盘上之后再一次性发给客户端，而是通过网络直接发个从节点。</p>
</blockquote>
<h3 id="增量复制"><a href="#增量复制" class="headerlink" title="增量复制"></a>增量复制</h3><p>由于全量复制在主节点数据量较大时效率太低，因此Redis2.8开始提供部分复制，用于处理网络中断时的数据同步。</p>
<blockquote>
<p>虽然不同版本的redis的复制有差异，不过前后兼容。</p>
</blockquote>
<p>部分复制的实现，依赖于三个重要的概念：</p>
<p>（1）<strong>复制偏移量</strong></p>
<p><strong>主节点和从节点分别维护一个复制偏移量（offset）</strong>，代表的是<strong>主节点向从节点传递的字节数</strong>；主节点每次向从节点传播N个字节数据时，主节点的offset增加N；从节点每次收到主节点传来的N个字节数据时，从节点的offset增加N。</p>
<p><strong>复制偏移量offset用于判断主从节点的数据库状态是否一致</strong>：如果二者offset相同，则一致；如果offset不同，则不一致，此时可以根据两个offset找出从节点缺少的那部分数据。</p>
<p>例如，如果主节点的offset是1000，而从节点的offset是500，那么部分复制就需要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置，就是下面要介绍的复制积压缓冲区。</p>
<p>（2）<strong>复制积压缓冲区</strong></p>
<p><strong>复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列</strong>，默认大小1MB；当主节点开始有从节点时创建，其作用是备份主节点最近发送给从节点的数据。<strong>注意，无论主节点有一个还是多个从节点，都只需要一个复制积压缓冲区</strong>。</p>
<p>在命令传播阶段，<strong>主节点除了将写命令发送给从节点，还会发送一份给复制积压缓冲区，作为写命令的备份</strong>；除了存储写命令，复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量（offset）。由于复制积压缓冲区定长且是先进先出，所以它保存的是主节点最近执行的写命令；<strong>时间较早的写命令会被挤出缓冲区</strong>。</p>
<p>由于该缓冲区长度固定且有限，因此可以备份的写命令也有限，<strong>当主从节点offset的差距过大超过缓冲区长度时，将无法执行部分复制，只能执行全量复制。</strong>反过来说，为了提高网络中断时部分复制执行的概率，可以根据需要增大复制积压缓冲区的大小(通过配置<code>repl-backlog-size</code>)；例如如果网络中断的平均时间是60s，而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB，则复制积压缓冲区的平均需求为6MB，保险起见，可以设置为12MB，来保证绝大多数断线情况都可以使用部分复制。</p>
<p><strong>从节点将自己的消费offset发送给主节点后，主节点根据offset和缓冲区大小决定能否执行部分复制</strong>：</p>
<ul>
<li>如果offset偏移量之后的数据，仍然都在复制积压缓冲区里，则执行部分复制；</li>
<li>如果offset偏移量之后的数据已不在复制积压缓冲区中（数据已被挤出），则执行全量复制。</li>
</ul>
<p>（3）<strong>服务器运行ID(runid)</strong></p>
<p>**每个Redis节点(无论主从)，在启动时都会自动生成一个随机ID(每次启动都不一样)**，由40个随机的十六进制字符组成；runid用来唯一识别一个Redis节点。通过info Server命令，可以查看节点的runid。</p>
<p>主从节点初次复制时，主节点将自己的runid发送给从节点，从节点将这个runid保存起来；</p>
<p><strong>当断线重连时，从节点会将这个runid发送给主节点；主节点根据runid判断能否进行部分复制</strong>：</p>
<ul>
<li>如果从节点保存的runid与主节点现在的runid相同，说明主从节点之前同步过，主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)；</li>
<li>如果从节点保存的runid与主节点现在的runid不同，说明从节点在断线前同步的Redis节点并不是当前的主节点，只能进行全量复制。</li>
</ul>
<h2 id="命令传播阶段（心跳机制）"><a href="#命令传播阶段（心跳机制）" class="headerlink" title="命令传播阶段（心跳机制）"></a>命令传播阶段（心跳机制）</h2><h3 id="主-gt-从-PING"><a href="#主-gt-从-PING" class="headerlink" title="主-&gt;从 : PING"></a>主-&gt;从 : PING</h3><p>每隔指定的时间，主节点会向从节点发送<code>PING</code>命令，这个PING命令的作用，主要是为了让从节点进行超时判断。</p>
<p>PING发送的频率由repl-ping-slave-period参数控制，单位是秒，默认值是10s。</p>
<p>关于该PING命令究竟是由主节点发给从节点，还是相反，有一些争议；因为在Redis的官方文档中，对该参数的注释中说明是从节点向主节点发送PING命令。</p>
<h3 id="从-gt-主：REPLCONF-ACK"><a href="#从-gt-主：REPLCONF-ACK" class="headerlink" title="从-&gt;主：REPLCONF ACK"></a>从-&gt;主：REPLCONF ACK</h3><p>在命令传播阶段，从节点会向主节点发送<code>REPLCONF ACK</code>命令，频率是每秒1次；命令格式为：<code>REPLCONF ACK &#123;offset&#125;</code>，其中offset指从节点保存的复制偏移量。REPLCONF ACK命令的作用包括：</p>
<p>（1）<strong>实时监测主从节点网络状态</strong>：<strong>该命令会被主节点用于复制超时的判断</strong>。此外，在主节点中使用info Replication，可以看到其从节点的状态中的lag值，代表的是主节点上次收到该REPLCONF ACK命令的时间间隔，在正常情况下，该值应该是0或1。</p>
<p>（2）<strong>检测命令丢失</strong>：从节点发送了自身的offset，主节点会与自己的offset对比，如果从节点数据缺失（如网络丢包），主节点会重新推送缺失的数据（这里也会利用复制积压缓冲区）。注意<strong>，offset和复制积压缓冲区，不仅可以用于部分复制，也可以用于处理命令丢失等情形</strong>；区别在于前者是在断线重连后进行的，而后者是在主从节点没有断线的情况下进行的。</p>
<p>（3）<strong>辅助保证从节点的数量和延迟</strong>：Redis主节点中使用<code>min-slaves-to-write</code>和<code>min-slaves-max-lag</code>参数，来保证主节点在不安全的情况下不会执行写命令；</p>
<p>所谓不安全，是指从节点数量太少，或延迟过高。</p>
<p>例如<code>min-slaves-to-write</code>和<code>min-slaves-max-lag</code>分别是3和10，含义是如果从节点数量小于3个，或所有从节点的延迟值都大于10s，则主节点拒绝执行写命令。</p>
<p>而这里从节点延迟值的获取，就是通过主节点接收到REPLCONF ACK命令的时间来判断的，即前面所说的info Replication中的lag值。</p>
<h1 id="实际应用的问题"><a href="#实际应用的问题" class="headerlink" title="实际应用的问题"></a>实际应用的问题</h1><h2 id="1-读写分离及其中的问题"><a href="#1-读写分离及其中的问题" class="headerlink" title="1. 读写分离及其中的问题"></a>1. 读写分离及其中的问题</h2><h3 id="（1）延迟与不一致问题"><a href="#（1）延迟与不一致问题" class="headerlink" title="（1）延迟与不一致问题"></a>（1）延迟与不一致问题</h3><p>由于<strong>主从复制的命令传播是异步的</strong>，延迟与数据的不一致不可避免。如果应用对数据不一致的接受程度程度较低，可能的优化措施包括：优化主从节点之间的网络环境（如在同机房部署）；监控主从节点延迟（通过offset）判断，<strong>如果从节点延迟过大，通知应用不再通过该从节点读取数据</strong>；<strong>使用集群同时扩展写负载和读负载等</strong>。</p>
<p>在命令传播阶段以外的其他情况下，从节点的数据不一致可能更加严重，例如连接在数据同步阶段，或从节点失去与主节点的连接时等。从节点的<code>slave-serve-stale-data</code>参数便与此有关：它控制这种情况下从节点的表现；如果为yes（默认值），则从节点仍能够响应客户端的命令，如果为no，则从节点只能响应info、slaveof等少数命令。该参数的设置与应用对数据一致性的要求有关；<strong>如果对数据一致性要求很高，则应设置为no</strong>。</p>
<h3 id="（2）数据过期问题"><a href="#（2）数据过期问题" class="headerlink" title="（2）数据过期问题"></a>（2）数据过期问题</h3><p>在单机版Redis中，存在两种删除策略：</p>
<ul>
<li>惰性删除：服务器不会主动删除数据，只有当客户端查询某个数据时，服务器判断该数据是否过期，如果过期则删除。</li>
<li>定期删除：服务器执行定时任务删除过期数据，但是考虑到内存和CPU的折中（删除会释放内存，但是频繁的删除操作对CPU不友好），该删除的频率和执行时间都受到了限制。</li>
</ul>
<p>在主从复制场景下，<strong>为了主从节点的数据一致性，从节点不会主动删除数据</strong>，而是由主节点控制从节点中过期数据的删除。由于主节点的惰性删除和定期删除策略，都不能保证主节点及时对过期数据执行删除操作，因此，当客户端通过Redis从节点读取数据时，很容易读取到已经过期的数据。</p>
<p><strong>Redis 3.2中，从节点在读取数据时，增加了对数据是否过期的判断</strong>：如果该数据已过期，则不返回给客户端；将Redis升级到3.2可以解决数据过期问题。</p>
<h3 id="（3）故障切换问题"><a href="#（3）故障切换问题" class="headerlink" title="（3）故障切换问题"></a>（3）故障切换问题</h3><p><strong>在没有使用哨兵的读写分离场景下</strong>，应用针对读和写分别连接不同的Redis节点；当主节点或从节点出现问题而发生更改时，需要及时修改应用程序读写Redis数据的连接；连接的切换可以手动进行，或者自己写监控程序进行切换，但前者响应慢、容易出错，后者实现复杂，成本都不算低。(所以后面有<strong>哨兵机制</strong>)</p>
<h2 id="2-各场景下复制的选择及优化技巧"><a href="#2-各场景下复制的选择及优化技巧" class="headerlink" title="2. 各场景下复制的选择及优化技巧"></a>2. 各场景下复制的选择及优化技巧</h2><p>在介绍了Redis复制的种种细节之后，现在我们可以来总结一下，在下面常见的场景中，何时使用部分复制，以及需要注意哪些问题。</p>
<h3 id="（1）第一次建立复制"><a href="#（1）第一次建立复制" class="headerlink" title="（1）第一次建立复制"></a>（1）第一次建立复制</h3><p>此时全量复制不可避免，但仍有几点需要注意：</p>
<p><strong>如果主节点的数据量较大，应该尽量避开流量的高峰期</strong>，避免造成阻塞；</p>
<p><strong>如果有多个从节点需要建立对主节点的复制，可以考虑将几个从节点错开</strong>，避免主节点带宽占用过大。</p>
<p><strong>如果从节点过多，也可以调整主从复制的拓扑结构，由一主多从结构变为树状结构</strong>（中间的节点既是其主节点的从节点，也是其从节点的主节点）；但使用树状结构应该谨慎：虽然主节点的直接从节点减少，降低了主节点的负担，但是多层从节点的延迟增大，数据一致性变差；且结构复杂，维护相当困难。</p>
<h3 id="（2）主节点重启"><a href="#（2）主节点重启" class="headerlink" title="（2）主节点重启"></a>（2）主节点重启</h3><p>主节点重启可以分为两种情况来讨论，一种是故障导致宕机，另一种则是有计划的重启。</p>
<p><strong>主节点宕机</strong></p>
<p>主节点宕机重启后，runid会发生变化，因此不能进行部分复制，只能全量复制。</p>
<p>实际上在主节点宕机的情况下，应进行故障转移处理，将其中的一个从节点升级为主节点，其他从节点从新的主节点进行复制；且故障转移应尽量的自动化，后面文章将要介绍的哨兵便可以进行自动的故障转移。</p>
<p><strong>安全重启：debug reload</strong></p>
<p>在一些场景下，可能希望对主节点进行重启，例如主节点内存碎片率过高，或者<strong>希望调整一些只能在启动时调整的参数</strong>。如果使用普通的手段重启主节点，会使得runid发生变化，可能导致不必要的全量复制。</p>
<p>为了解决这个问题，Redis提供了<code>debug reload</code>的重启方式：重启后，主节点的runid和offset都不受影响，避免了全量复制。</p>
<p>如下图所示，debug reload重启后runid和offset都未受影响：</p>
<h3 id="（3）从节点重启"><a href="#（3）从节点重启" class="headerlink" title="（3）从节点重启"></a>（3）从节点重启</h3><p>从节点宕机重启后，其保存的主节点的runid会丢失，因此即使再次执行slaveof，也无法进行部分复制。</p>
<h3 id="（4）网络中断"><a href="#（4）网络中断" class="headerlink" title="（4）网络中断"></a>（4）网络中断</h3><p>如果主从节点之间出现网络问题，造成短时间内网络中断，可以分为多种情况讨论。</p>
<p>第一种情况：网络问题时间极为短暂，只造成了短暂的丢包，主从节点都没有判定超时（未触发repl-timeout）；此时只需要通过REPLCONF ACK来补充丢失的数据即可。</p>
<p>第二种情况：网络问题时间很长，主从节点判断超时（触发了repl-timeout），且丢失的数据过多，超过了复制积压缓冲区所能存储的范围；此时主从节点无法进行部分复制，只能进行全量复制。<strong>为了尽可能避免这种情况的发生，应该根据实际情况适当调整复制积压缓冲区的大小</strong>；此外及时发现并修复网络中断，也可以减少全量复制。</p>
<p>第三种情况：介于前述两种情况之间，主从节点判断超时，且丢失的数据仍然都在复制积压缓冲区中；此时主从节点可以进行部分复制。</p>
<h2 id="3-复制相关的配置"><a href="#3-复制相关的配置" class="headerlink" title="3. 复制相关的配置"></a>3. 复制相关的配置</h2><p>这一节总结一下与复制有关的配置，说明这些配置的作用、起作用的阶段，以及配置方法等；通过了解这些配置，一方面加深对Redis复制的了解，另一方面掌握这些配置的方法，可以优化Redis的使用，少走坑。</p>
<p>配置大致可以分为主节点相关配置、从节点相关配置以及与主从节点都有关的配置，下面分别说明。</p>
<h3 id="（1）与主从节点都有关的配置"><a href="#（1）与主从节点都有关的配置" class="headerlink" title="（1）与主从节点都有关的配置"></a>（1）与主从节点都有关的配置</h3><p>首先介绍最特殊的配置，它决定了该节点是主节点还是从节点：</p>
<ol>
<li><p>slaveof masterip masterport：Redis启动时起作用；作用是建立复制关系，开启了该配置的Redis服务器在启动后成为从节点。该注释默认注释掉，即Redis服务器默认都是主节点。</p>
</li>
<li><p>repl-timeout 60：与各个阶段<strong>主从节点连接超时判断</strong>有关，见前面的介绍。</p>
</li>
</ol>
<h3 id="（2）主节点相关配置"><a href="#（2）主节点相关配置" class="headerlink" title="（2）主节点相关配置"></a>（2）主节点相关配置</h3><ol>
<li><p>repl-diskless-sync no：作用于全量复制阶段，控制主节点是否使用diskless复制（无盘复制）。所谓diskless复制，是指在全量复制时，主节点不再先把数据写入RDB文件，而是直接写入slave的socket中，整个过程中不涉及硬盘；diskless复制在磁盘IO很慢而网速很快时更有优势。需要注意的是，截至Redis3.0，diskless复制处于实验阶段，默认是关闭的。</p>
</li>
<li><p>repl-diskless-sync-delay 5：该配置作用于全量复制阶段，当主节点使用diskless复制时，该配置决定主节点向从节点发送之前停顿的时间，单位是秒；只有当diskless复制打开时有效，默认5s。之所以设置停顿时间，是基于以下两个考虑：(1)向slave的socket的传输一旦开始，新连接的slave只能等待当前数据传输结束，才能开始新的数据传输 (2)多个从节点有较大的概率在短时间内建立主从复制。</p>
</li>
<li><p>client-output-buffer-limit slave 256MB 64MB 60：与<strong>全量复制阶段主节点的缓冲区大小</strong>有关，见前面的介绍。</p>
</li>
<li><p>repl-disable-tcp-nodelay no：与<strong>命令传播阶段的延迟</strong>有关，见前面的介绍。</p>
</li>
<li><p>masterauth master-password：与连接建立阶段的身份验证有关，见前面的介绍。</p>
</li>
<li><p>repl-ping-slave-period 10：与<strong>命令传播阶段主从节点的超时判断</strong>有关，见前面的介绍。</p>
</li>
<li><p>repl-backlog-size 1mb：<strong>复制积压缓冲区的大小</strong>，见前面的介绍。</p>
</li>
<li><p>repl-backlog-ttl 3600：当主节点没有从节点时，复制积压缓冲区保留的时间，这样当断开的从节点重新连进来时，可以进行部分复制；默认3600s。如果设置为0，则永远不会释放复制积压缓冲区。</p>
</li>
<li><p>min-slaves-to-write 3：规定了<strong>主节点的最小从节点数目</strong>。</p>
</li>
<li><p>min-slaves-max-lag 10：<strong>从节点对应的最大延迟</strong>，见前面的介绍。</p>
</li>
</ol>
<h3 id="（3）从节点相关配置"><a href="#（3）从节点相关配置" class="headerlink" title="（3）从节点相关配置"></a>（3）从节点相关配置</h3><ol>
<li><p>slave-serve-stale-data yes：与<strong>从节点数据陈旧时是否响应客户端命令</strong>有关，见前面的介绍。</p>
</li>
<li><p>slave-read-only yes：<strong>从节点是否只读</strong>；默认是只读的。由于从节点开启写操作容易导致主从节点的数据不一致，因此该配置尽量不要修改。</p>
</li>
</ol>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/kismetv/p/9236731.html"><a target="_blank" rel="noopener" href="https://www.cnblogs.com/kismetv/p/9236731.html">深入学习Redis（3）：主从复制 </a></a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/08/LeetCode-%E5%8D%96%E8%82%A1%E7%A5%A8%E5%85%AD%E9%81%93%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/08/LeetCode-%E5%8D%96%E8%82%A1%E7%A5%A8%E5%85%AD%E9%81%93%E9%A2%98/" class="post-title-link" itemprop="url">LeetCode 卖股票六道题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-08 18:43:53" itemprop="dateCreated datePublished" datetime="2021-12-08T18:43:53+08:00">2021-12-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:23:52" itemprop="dateModified" datetime="2023-01-03T20:23:52+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">算法题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="121-买卖股票的最佳时机"><a href="#121-买卖股票的最佳时机" class="headerlink" title="121.买卖股票的最佳时机"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/submissions/">121.买卖股票的最佳时机</a></h1><p>DP的阶段就是天数，记录下历史最低价格，用新出现的价格更新答案，并更新历史最小值。<br>时间复杂度：<br>$O(n)$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; prices)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>, m = INT_MAX;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;prices.<span class="built_in">size</span>();i++)&#123;</span><br><span class="line">            ans = <span class="built_in">max</span>(ans,prices[i]-m);</span><br><span class="line">            m = <span class="built_in">min</span>(m,prices[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这里提供一个更直观的解法。<br>时间复杂度：$O(n*log(n))$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; prices)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">        multiset&lt;<span class="type">int</span>&gt; ms;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x:prices)&#123;</span><br><span class="line">            ms.<span class="built_in">insert</span>(x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;prices.<span class="built_in">size</span>();i++)&#123;</span><br><span class="line">            ms.<span class="built_in">erase</span>(ms.<span class="built_in">find</span>(prices[i]));</span><br><span class="line">            <span class="keyword">if</span>(!ms.<span class="built_in">empty</span>()) ans = <span class="built_in">max</span>(ans,*ms.<span class="built_in">rbegin</span>()-prices[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h1 id="122-买卖股票的最佳时机-II"><a href="#122-买卖股票的最佳时机-II" class="headerlink" title="122. 买卖股票的最佳时机 II"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/">122. 买卖股票的最佳时机 II</a></h1><p>累积每个单调升序端一头一尾的差值。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span> , n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="type">int</span> l = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(a[i]&lt;a[i<span class="number">-1</span>])&#123;</span><br><span class="line">                ans += a[i<span class="number">-1</span>]-a[l];</span><br><span class="line">                l = i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ans += <span class="built_in">max</span>(<span class="number">0</span>,a[n<span class="number">-1</span>]-a[l]);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>有点<strong>差分</strong>的感觉。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span> ;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;a.<span class="built_in">size</span>();i++)&#123;</span><br><span class="line">            ans += <span class="built_in">max</span>(<span class="number">0</span>,a[i]-a[i<span class="number">-1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<ul>
<li>DP<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">dp</span>(n,<span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(<span class="number">2</span>));</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>] = -a[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            dp[i][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>],dp[i<span class="number">-1</span>][<span class="number">1</span>]+a[i]);</span><br><span class="line">            dp[i][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>],dp[i<span class="number">-1</span>][<span class="number">0</span>]-a[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n<span class="number">-1</span>][<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="714-买卖股票的最佳时机含手续费"><a href="#714-买卖股票的最佳时机含手续费" class="headerlink" title="714. 买卖股票的最佳时机含手续费"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/">714. 买卖股票的最佳时机含手续费</a></h1><p>上一问的DP的延续。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a, <span class="type">int</span> fee)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">dp</span>(n,<span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(<span class="number">2</span>));</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>] = -a[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            dp[i][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>],dp[i<span class="number">-1</span>][<span class="number">1</span>]+a[i]-fee);</span><br><span class="line">            dp[i][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>],dp[i<span class="number">-1</span>][<span class="number">0</span>]-a[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n<span class="number">-1</span>][<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h1 id="123-买卖股票的最佳时机-III"><a href="#123-买卖股票的最佳时机-III" class="headerlink" title="123. 买卖股票的最佳时机 III"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/">123. 买卖股票的最佳时机 III</a></h1><p>自己的代码，但是思路并不是很清晰，还WA了好几次，主要是初值设置。</p>
<ul>
<li>截取了一段网友的思路，很清晰：<br>一天结束时，可能有持股、可能未持股、可能卖出过1次、可能卖出过2次、也可能未卖出过<br>所以定义状态转移数组dp[天数][当前是否持股][卖出的次数]</li>
</ul>
<p>具体一天结束时的6种状态：<br>① 未持股，未卖出过股票：说明从未进行过买卖，利润为0<br>$dp[i][0][0]&#x3D;0$<br>② 未持股，卖出过1次股票：可能是今天卖出，也可能是之前卖的（昨天也未持股且卖出过）<br>$dp[i][0][1]&#x3D;max(dp[i-1][1][0]+prices[i],dp[i-1][0][1])$<br>③ 未持股，卖出过2次股票:可能是今天卖出，也可能是之前卖的（昨天也未持股且卖出过）<br>$dp[i][0][2]&#x3D;max(dp[i-1][1][1]+prices[i],dp[i-1][0][2])$<br>④ 持股，未卖出过股票：可能是今天买的，也可能是之前买的（昨天也持股）<br>$dp[i][1][0]&#x3D;max(dp[i-1][0][0]-prices[i],dp[i-1][1][0])$<br>⑤ 持股，卖出过1次股票：可能是今天买的，也可能是之前买的（昨天也持股）<br>$dp[i][1][1]&#x3D;max(dp[i-1][0][1]-prices[i],dp[i-1][1][1])$<br>⑥ 持股，卖出过2次股票：最多交易2次，这种情况不存在<br>$dp[i][1][2]&#x3D;float(‘-inf’)$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        vector&lt;vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&gt; <span class="built_in">dp</span>(n,vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;(<span class="number">2</span>,<span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(<span class="number">3</span>,<span class="number">0</span>)));</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>][<span class="number">0</span>] = -a[<span class="number">0</span>];</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>][<span class="number">1</span>] = <span class="number">-1e9</span>;</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>][<span class="number">2</span>] = <span class="number">-1e9</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">0</span>;k&lt;<span class="number">2</span>;k++)&#123;</span><br><span class="line">                dp[i][<span class="number">0</span>][k+<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>][k+<span class="number">1</span>],dp[i<span class="number">-1</span>][<span class="number">1</span>][k]+a[i]);</span><br><span class="line">                dp[i][<span class="number">1</span>][k] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>][k],dp[i<span class="number">-1</span>][<span class="number">0</span>][k]-a[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(<span class="built_in">max</span>(dp[n<span class="number">-1</span>][<span class="number">0</span>][<span class="number">1</span>],dp[n<span class="number">-1</span>][<span class="number">0</span>][<span class="number">2</span>]),<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h1 id="188-买卖股票的最佳时机-IV"><a href="#188-买卖股票的最佳时机-IV" class="headerlink" title="188. 买卖股票的最佳时机 IV"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/">188. 买卖股票的最佳时机 IV</a></h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(<span class="type">int</span> K, vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(K&gt;=n/<span class="number">2</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">maxProfit</span>(a);</span><br><span class="line">        &#125;        </span><br><span class="line">        vector&lt;vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&gt; <span class="built_in">dp</span>(n,vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;(<span class="number">2</span>,<span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(K+<span class="number">1</span>,<span class="number">0</span>)));</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>][<span class="number">0</span>] = -a[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">1</span>;k&lt;=K;k++)&#123;</span><br><span class="line">            dp[<span class="number">0</span>][<span class="number">1</span>][k] = <span class="number">-1e9</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">0</span>;k&lt;K;k++)&#123;</span><br><span class="line">                dp[i][<span class="number">0</span>][k+<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>][k+<span class="number">1</span>],dp[i<span class="number">-1</span>][<span class="number">1</span>][k]+a[i]);</span><br><span class="line">                dp[i][<span class="number">1</span>][k] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>][k],dp[i<span class="number">-1</span>][<span class="number">0</span>][k]-a[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">1</span>;k&lt;=K;k++)&#123;</span><br><span class="line">            ans = <span class="built_in">max</span>(ans,dp[n<span class="number">-1</span>][<span class="number">0</span>][k]);</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span> ;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;a.<span class="built_in">size</span>();i++)&#123;</span><br><span class="line">            ans += <span class="built_in">max</span>(<span class="number">0</span>,a[i]-a[i<span class="number">-1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h1 id="309-最佳买卖股票时机含冷冻期"><a href="#309-最佳买卖股票时机含冷冻期" class="headerlink" title="309.最佳买卖股票时机含冷冻期"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/">309.最佳买卖股票时机含冷冻期</a></h1><p>用<code>dp[i][j][k]</code>其中i是角标;<br>j为0或1——表示这一天结束时是否拥有股票;<br>k也为0或1，表示这一天是否是交易日。<br>初始化为负无穷大。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        vector&lt;vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&gt; <span class="built_in">dp</span>(n,vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;(<span class="number">2</span>,<span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(<span class="number">2</span>,<span class="number">-1e9</span>)));</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">1</span>][<span class="number">0</span>] = -a[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            <span class="comment">// 买股票</span></span><br><span class="line">            dp[i][<span class="number">1</span>][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>][<span class="number">0</span>],dp[i<span class="number">-1</span>][<span class="number">0</span>][<span class="number">0</span>]-a[i]);</span><br><span class="line">            <span class="comment">// 卖股票</span></span><br><span class="line">            dp[i][<span class="number">0</span>][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>][<span class="number">0</span>],dp[i<span class="number">-1</span>][<span class="number">0</span>][<span class="number">1</span>]);</span><br><span class="line">            dp[i][<span class="number">0</span>][<span class="number">1</span>] = dp[i<span class="number">-1</span>][<span class="number">1</span>][<span class="number">0</span>]+a[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(dp[n<span class="number">-1</span>][<span class="number">0</span>][<span class="number">0</span>],dp[n<span class="number">-1</span>][<span class="number">0</span>][<span class="number">1</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>很明显空间可以进行一下压缩。但是可读性有点差了</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxProfit</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(n&lt;<span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> dp00 = <span class="number">0</span>,dp10 = -a[<span class="number">0</span>],dp01=<span class="number">-1e9</span>,dp_00,dp_01,dp_10;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">            <span class="comment">// 买股票</span></span><br><span class="line">            dp_10 = <span class="built_in">max</span>(dp10,dp00-a[i]);</span><br><span class="line">            <span class="comment">// 卖股票</span></span><br><span class="line">            dp_00 = <span class="built_in">max</span>(dp00,dp01);</span><br><span class="line">            dp_01 = dp10+a[i];</span><br><span class="line">            dp10 = dp_10;</span><br><span class="line">            dp00 = dp_00;</span><br><span class="line">            dp01 = dp_01;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(dp00,dp01);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/07/Java%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E6%96%B9%E5%BC%8F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/07/Java%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E6%96%B9%E5%BC%8F/" class="post-title-link" itemprop="url">Java对象的四种引用方式</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-07 01:42:49" itemprop="dateCreated datePublished" datetime="2021-12-07T01:42:49+08:00">2021-12-07</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-08 19:28:11" itemprop="dateModified" datetime="2021-12-08T19:28:11+08:00">2021-12-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Reference</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">//引用的对象</span></span><br><span class="line">    <span class="keyword">private</span> T referent;        </span><br><span class="line">    <span class="comment">//回收队列，由使用者在Reference的构造函数中指定</span></span><br><span class="line">    <span class="keyword">volatile</span> ReferenceQueue&lt;? <span class="built_in">super</span> T&gt; queue;</span><br><span class="line">     <span class="comment">//当该引用被加入到queue中的时候，该字段被设置为queue中的下一个元素，以形成链表结构</span></span><br><span class="line">    <span class="keyword">volatile</span> Reference next;</span><br><span class="line">    <span class="comment">//在GC时，JVM底层会维护一个叫DiscoveredList的链表，存放的是Reference对象，discovered字段指向的就是链表中的下一个元素，由JVM设置</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="keyword">private</span> Reference&lt;T&gt; discovered;  </span><br><span class="line">    <span class="comment">//进行线程同步的锁对象</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">private</span> <span class="keyword">class</span> <span class="title class_">Lock</span> &#123; &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Lock</span>();</span><br><span class="line">    <span class="comment">//等待加入queue的Reference对象，在GC时由JVM设置，会有一个java层的线程(ReferenceHandler)源源不断的从pending中提取元素加入到queue</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Reference&lt;Object&gt; pending = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<p>一个Reference对象的生命周期如下：</p>
<p><img src="/2021/12/07/Java%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E6%96%B9%E5%BC%8F/java-ref.png"></p>
<h1 id="1-强引用"><a href="#1-强引用" class="headerlink" title="1.强引用"></a>1.强引用</h1><p>我们平日里面的用到的new了一个对象就是强引用，例如 <code>Object obj = new Object();</code></p>
<p>当JVM的内存空间不足时，宁愿抛出OutOfMemoryError使得程序异常终止也不愿意回收具有<strong>强引用的存活着的对象</strong>！</p>
<p>回收的时间是不一定的，具体得看GC回收策略。</p>
<h1 id="2-软引用"><a href="#2-软引用" class="headerlink" title="2.软引用"></a>2.软引用</h1><p>软引用的生命周期比强引用短一些。软引用是通过<code>SoftReference</code>类实现的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="type">SoftReference</span> <span class="variable">softObj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SoftReference</span>(obj);</span><br><span class="line">obj = <span class="literal">null</span>； <span class="comment">//去除强引用</span></span><br></pre></td></tr></table></figure>



<p>这样就是一个简单的软引用使用方法。通过<code>get()</code>方法获取对象。</p>
<p><strong>当JVM认为内存空间不足时，就回去试图回收软引用指向的对象</strong>，也就是说在JVM抛出<code>OutOfMemoryError</code>之前，会去清理软引用对象。</p>
<p>软引用可以与引用队列<code>(ReferenceQueue)</code>联合使用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="type">ReferenceQueue</span> <span class="variable">queue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReferenceQueue</span>();</span><br><span class="line"><span class="type">SoftReference</span> <span class="variable">softObj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SoftReference</span>(obj,queue);</span><br><span class="line">obj = <span class="literal">null</span>； <span class="comment">//去除强引用</span></span><br></pre></td></tr></table></figure>



<p>当<code>softObj</code>软引用的<code>obj</code>被GC回收之后，<code>softObj</code> 对象就会被塞到<code>queue</code>中，之后我们可以通过这个队列的<code>poll()</code>来检查你关心的对象是否被回收了，如果队列为空，就返回一个<code>null</code>。反之就返回软引用对象也就是<code>softObj</code>。</p>
<p><strong>软引用一般用来实现内存敏感的缓存</strong>，如果有空闲内存就可以保留缓存，当内存不足时就清理掉，这样就保证使用缓存的同时不会耗尽内存。例如图片缓存框架中缓存图片就是通过软引用的。</p>
<h1 id="3-弱引用"><a href="#3-弱引用" class="headerlink" title="3.弱引用"></a>3.弱引用</h1><p>弱引用是通过<code>WeakReference</code>类实现的，它的生命周期比软引用还要短, 也是通过<code>get()</code>方法获取对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">WeakReference&lt;Object&gt; weakObj = <span class="keyword">new</span> <span class="title class_">WeakReference</span>&lt;Object&gt;(obj);</span><br><span class="line">obj = <span class="literal">null</span>； <span class="comment">//去除强引用</span></span><br></pre></td></tr></table></figure>



<p><strong>在GC的时候，JVM 不管内存空间足不足都会回收这个对象</strong>，同样也可以配合<code>ReferenceQueue</code> 使用，也同样适用于内存敏感的缓存。</p>
<p><code>ThreadLocal</code>中的<code>Entry</code>就是继承了<code>WeakReference</code>类，这<code>Entry</code>弱引用着<code>ThreadLocal</code>。</p>
<h1 id="4-幻象引用"><a href="#4-幻象引用" class="headerlink" title="4.幻象引用"></a>4.幻象引用</h1><p>也称虚引用，是通过<code>PhantomReference</code>类实现的。任何时候可能被GC回收,就像没有引用一样。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="type">ReferenceQueue</span> <span class="variable">queue</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReferenceQueue</span>();</span><br><span class="line">PhantomReference&lt;Object&gt; phantomObj = <span class="keyword">new</span> <span class="title class_">PhantomReference</span>&lt;Object&gt;(obj , queue);</span><br><span class="line">obj = <span class="literal">null</span>； <span class="comment">//去除强引用</span></span><br></pre></td></tr></table></figure>



<p>不过<strong>无法通过虚引用访问对象的任何属性或者函数</strong>。</p>
<p>那就要问了要它有什么用？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PhantomReference</span>&lt;T&gt; <span class="keyword">extends</span> <span class="title class_">Reference</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">// 永远返回 null</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>虚引用仅仅只是提供了一种确保对象被<code>finalize</code>以后来做某些事情的机制。比如说这个对象被回收之后发一个系统通知啊啥的。虚引用是必须配合<code>ReferenceQueue</code> 使用的，具体使用方法和上面提到软引用的一样。主要用来跟踪对象被垃圾回收的活动。</p>
<p>一个错误认知：</p>
<blockquote>
<p>严格的说，<strong>虚引用是会影响对象生命周期的</strong>，如果不做任何处理，只要虚引用不被回收，那其引用的对象永远不会被回收。所以一般来说，从ReferenceQueue中获得PhantomReference对象后，如果PhantomReference对象不会被回收的话（比如被其他GC ROOT可达的对象引用），需要调用clear方法解除PhantomReference和其引用对象的引用关系。</p>
</blockquote>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000039994284"><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000039994284">Java四种引用类型原理你真的搞明白了吗？五分钟带你深入理解！</a></a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/06/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E2%80%94%E5%A0%86%E5%92%8C%E6%A0%88/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/06/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E2%80%94%E5%A0%86%E5%92%8C%E6%A0%88/" class="post-title-link" itemprop="url">操作系统——堆和栈</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-06 15:56:01" itemprop="dateCreated datePublished" datetime="2021-12-06T15:56:01+08:00">2021-12-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-23 01:30:20" itemprop="dateModified" datetime="2022-09-23T01:30:20+08:00">2022-09-23</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" itemprop="url" rel="index"><span itemprop="name">操作系统</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="进程内存的段式划分"><a href="#进程内存的段式划分" class="headerlink" title="进程内存的段式划分"></a>进程内存的段式划分</h1><p>一个由C&#x2F;C++编译的程序占用的内存分为以下几个部分<br>（从上到下，<strong>从内存高地址到内存低地址</strong>）<br>1、栈区（stack） — 由编译器自动分配释放 ，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。（高地址）</p>
<p>2、堆区（heap） — 一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表（当然还有其它的管理方式）。</p>
<p>3、全局区（静态区）（static）—全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域， 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。</p>
<p>4、文字常量区 — 常量字符串就是放在这里的。 程序结束后由系统释放。</p>
<p>5、程序代码区——存放函数体的二进制代码。（低地址）</p>
<p>下面是一个例子：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> a = <span class="number">0</span>; <span class="comment">//全局初始化区</span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> *p1; <span class="comment">//全局未初始化区</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> b; <span class="comment">//栈</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> s[] = <span class="string">&quot;abc&quot;</span>; <span class="comment">//s在栈,&quot;abc&quot;在常量区</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *p2; <span class="comment">//栈</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *p3 = <span class="string">&quot;123456&quot;</span>; <span class="comment">// 123456在常量区，p3在栈上。</span></span><br><span class="line"></span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> c = <span class="number">0</span>; <span class="comment">//全局（静态）初始化区</span></span><br><span class="line"></span><br><span class="line">    p1 = (<span class="type">char</span> *) <span class="built_in">malloc</span>(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">    p2 = (<span class="type">char</span> *) <span class="built_in">malloc</span>(<span class="number">20</span>); <span class="comment">// 分配的10和20字节的区域就在堆区</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="堆和栈的区别"><a href="#堆和栈的区别" class="headerlink" title="堆和栈的区别"></a>堆和栈的区别</h1><h2 id="申请方式"><a href="#申请方式" class="headerlink" title="申请方式"></a>申请方式</h2><p><strong>stack:</strong></p>
<p>由系统自动分配。<br>例如，声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间</p>
<p><strong>heap:</strong></p>
<p>需要程序员自己申请，并指明大小，在c中malloc函数</p>
<p>如<code>p1 = (char *)malloc(10)</code>;</p>
<p>在C++中用new运算符, 如 <code>p2 = new char[10]</code>;</p>
<p>但是注意p1、p2本身是在栈中的。</p>
<h2 id="申请后系统的响应"><a href="#申请后系统的响应" class="headerlink" title="申请后系统的响应"></a>申请后系统的响应</h2><p><strong>栈：</strong>只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。</p>
<p><strong>堆：</strong>首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的delete语句才能正确的释放本内存空间。<br>另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</p>
<h2 id="申请大小的限制"><a href="#申请大小的限制" class="headerlink" title="申请大小的限制"></a>申请大小的限制</h2><p><strong>栈：</strong>栈是<strong>向低地址扩展</strong>的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译时就确定的常数），在Linux默认是8M。如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。</p>
<p><strong>堆：</strong>堆是<strong>向高地址扩展</strong>的数据结构，是不连续的内存区域。这是由于系统是用链表（还有其他综合手段）来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。</p>
<h2 id="申请效率的比较"><a href="#申请效率的比较" class="headerlink" title="申请效率的比较"></a>申请效率的比较</h2><p><strong>栈</strong>由系统自动分配，速度较快。但程序员是无法控制的。</p>
<p><strong>堆</strong>是由new分配的内存，一般速度比较慢，而且容易产生内存碎片,不过用起来最方便.另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一块内存，虽然用起来最不方便。但是速度快，也最灵活。</p>
<h2 id="堆和栈中的存储内容"><a href="#堆和栈中的存储内容" class="headerlink" title="堆和栈中的存储内容"></a>堆和栈中的存储内容</h2><p><strong>栈：</strong> 在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。</p>
<p><strong>堆：</strong>一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。</p>
<h2 id="堆和栈中的存储内容-1"><a href="#堆和栈中的存储内容-1" class="headerlink" title="堆和栈中的存储内容"></a>堆和栈中的存储内容</h2><p><code>char s1[] = “aaaaaaaaaaaaaaa”;</code></p>
<p><code>char *s2 = “bbbbbbbbbbbbbbbbb”;</code></p>
<p>aaaaaaaaaaa是在运行时刻赋值的；</p>
<p>而bbbbbbbbbbb是在编译时就确定的；</p>
<p>但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。</p>
<p>比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">int main() &#123;</span><br><span class="line"></span><br><span class="line">    char a = 1;</span><br><span class="line"></span><br><span class="line">    char c[] = &quot;1234567890&quot;;</span><br><span class="line"></span><br><span class="line">    char *p = &quot;1234567890&quot;;</span><br><span class="line"></span><br><span class="line">    a = c[1];</span><br><span class="line"></span><br><span class="line">    a = p[1];</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对应的汇编代码</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">10: a = c[1];</span><br><span class="line"></span><br><span class="line">00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]</span><br><span class="line"></span><br><span class="line">0040106A 88 4D FC mov byte ptr [ebp-4],cl</span><br><span class="line"></span><br><span class="line">11: a = p[1];</span><br><span class="line"></span><br><span class="line">0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]</span><br><span class="line"></span><br><span class="line">00401070 8A 42 01 mov al,byte ptr [edx+1]</span><br><span class="line"></span><br><span class="line">00401073 88 45 FC mov byte ptr [ebp-4],al</span><br></pre></td></tr></table></figure>



<p>第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，再根据edx读取字符串，显然慢了。</p>
<h1 id="堆的动态管理"><a href="#堆的动态管理" class="headerlink" title="堆的动态管理"></a>堆的动态管理</h1><h2 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h2><p>堆是一块巨大的内存空间，常常占据着整个<strong>虚拟空间</strong>的绝大部分。在这片空间里，程序可以请求一块连续内存，并自由地使用，这块内存在程序主动放弃之前都会一直保持有效。下面是申请空间最简单的例子。　</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *p = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">1000</span>);</span><br><span class="line">    <span class="built_in">free</span> (p)&#x27;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>进程的内存管理并没有交给操作系统内核管理</strong>，这样做性能较差，因为每次程序申请或者释放对空间都要进行系统调用。我们知道系统调用的性能开销是很大的，当程序对堆的操作比较频繁时，这样做的结果是会严重影响程序性能的。比较好的做法就是程序向操作系统申请一块适当大小的堆空间，然后由程序自己管理这块空间，而具体来讲，<strong>管理着堆空间分配往往是程序的运行库。</strong></p>
<blockquote>
<p>运行库相当于向操作系统批发了一块较大的堆空间，然后“零售”给程序用。当全部“售完”或程序有大量的内存需求时，在根据实际需求向操作系统“进货”。当然运行库在向零售堆空间时，必须管理它批发来的堆空间，不能把同一块地址出售两次，导致地址的冲突。我们首先来了解运行库是怎么向操作系统批发内存的。我们以linux为例。</p>
</blockquote>
<h2 id="Linux进程堆管理"><a href="#Linux进程堆管理" class="headerlink" title="Linux进程堆管理"></a>Linux进程堆管理</h2><p>　进程地址空间中，除了可执行文件、共享库和栈之外，剩余的未分配的空间都可以被用来作为堆空间。Linux下的进程管理稍微有些复杂，因为它提供了两种堆分配方式，即两个系统调用：一个是brk()系统调用，另外一个是mmap()。brk()的C语言形式声明如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">brk</span><span class="params">(<span class="type">void</span>* end_data_segment)</span></span><br></pre></td></tr></table></figure>

<p>　　</p>
<p><strong>brk()的作用实际上就是设置进程数据段的结束地址，即它可以扩大或者缩小数据段</strong>（Linux下数据段和BSS合并在一起统称为数据段）。如果我们将数据段的结束地址向高地址移动，那么扩大的那部分空间就可以被我们使用，把这块空间拿来作为堆空间是最常见的做法之一。Giblic中还有一个函数叫做sbrk，它的功能与brk类似，只不过参数和返回值略有不同。sbrk以一个增量作为参数，即需要增加（负数为减少）的空间大小，返回值是增加（或减少）后数据段结束地址，这个函数实际上是对brk系统调用的包装，它通过brk()实现的。</p>
<p>　　mmap()的作用和Windows系统下的VirtualAlloc很相似，它的作用就是向操作系统申请一段虚拟地址空间，当然这块虚拟地址空间可以映射到某个文件（这也是系统调用的最初的作用），当它不将地址空间映射到某个文件时，我们又称这块空间为匿名空间，匿名空间就可以拿来做堆空间。它的声明如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> *mmap&#123;<span class="type">void</span> *start,  <span class="type">size_t</span> length, <span class="type">int</span> prot, <span class="type">int</span> flags, <span class="type">int</span> fd,<span class="type">off_t</span> offset);</span><br></pre></td></tr></table></figure>

<p>　　mmap的前两个参数分别用于指定需要申请的空间的起始地址和长度，如果起始地址设置为0，那么linux系统会自动挑选合适的起始地址。prot&#x2F;flags这两个参数用于设置申请的空间的权限（可读，可写，可执行）以及映像类型（文件映射、匿名空间等），最后两个参数用于文件映射时指定文件描述符和文件偏移的，我们在这里并不关心它们。</p>
<p><strong>glibc的malloc函数是这样处理用户空间请求的</strong></p>
<ol>
<li>对于小于128kb的请求来说，它会在现有的堆空间里面，按照堆分配算法为它分配一块空间并返回；</li>
<li>对于大于128KB的请求来说，它会使用mmap()函数为它分配一块匿名空间，然后再这个匿名空间中为用户分配空间。</li>
</ol>
<p>当然我们直接使用mmap也可以轻而易举地实现malloc函数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> *<span class="title function_">malloc</span><span class="params">(<span class="type">size_t</span> nbytes)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">void</span> *ret = mmap(<span class="number">0</span>, nbytes, PROT_READ | PROT_WRITE,</span><br><span class="line">                               MAP_PRIVATE | MAP_ANONYMOUS, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (ret == MAP_FAILED)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>由于mmap()函数与VirtualAlloc()类似，它们都是系统虚拟空间申请函数，它们申请的空间起始地址和大小都必须是系统页的大小的整数倍。</p>
<h2 id="堆空间的管理算法"><a href="#堆空间的管理算法" class="headerlink" title="堆空间的管理算法"></a>堆空间的管理算法</h2><p>在动态分配内存后，那么我们就要来思考如何管理这块大的内存。主要有三种方法，空闲链表和位图法以及对象池。</p>
<h3 id="空闲链表"><a href="#空闲链表" class="headerlink" title="空闲链表"></a>空闲链表</h3><p>空闲链表（Free List)的方法实际上就是把堆中各个空闲的块按照链表的方式连接起来，当用户请求一块空间时，可以遍历整个链表，直到找到合适大小的块并且将它拆分；当用户释放空间时将它合并到空闲链表中。</p>
<p>空闲链表是这样一种结构，在堆里的每一个空闲空间的开头（或结尾）有一个头，头结构里记录了上一个和下一个空闲块的地址，也就是说，所有的空闲块形成了一个链表。如下所示：</p>
<p><img src="/2021/12/06/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E2%80%94%E5%A0%86%E5%92%8C%E6%A0%88/list.png"></p>
<p>当按照地址顺序在链表中存放进程和空闲区时，有几种算法可以用来为创建的进程（从磁盘换入的已存在的内存）分配内存。当存储管理器知道要为进程分配多大的内存时，有如下几种算法。</p>
<ol>
<li><strong>首次适配（first fit)算法</strong></li>
</ol>
<p>存储管理器沿着段链表进行搜索，直到找到一个足够大的空闲区，除非空闲区大小和要分配的空间大小正好一样，否则将该空闲去分为两部分，一部分供进程使用，另一部分形成新的空闲区。首次适配算法是一种速度很快的算法，因为它尽可能少地搜索链表节点。</p>
<ol start="2">
<li><strong>下次适配（next fit）算法</strong></li>
</ol>
<p>它的工作方式和首次适配算法不同，不同点是每次找到合适的区间都记录当时的位置。以便在下次寻访空闲区时从上次结束的地方开始搜索，而不是像首次适配算法那样每次从头开始。下次适配算法的性能略低于首次适配算法。</p>
<ol start="3">
<li><strong>最佳适配（best fit）算法</strong></li>
</ol>
<p>最佳适配算法搜索整个链表，找出能够容纳进程的最小的空闲区。最佳适配算法师徒找出最接近实际需要的空闲区，以最好地匹配请求和可用空闲区，而不是先拆分一个以后可能会用到的最大的空闲区。但是它的缺点是产生较多的业内碎片。</p>
<ol start="4">
<li><strong>最差适配（worst fit）算法</strong></li>
</ol>
<p>总是分配最大的可用空闲区。</p>
<ol start="5">
<li><strong>快速适配（quick fit）算法</strong></li>
</ol>
<p>它为那些常用大小的空闲区维护单独的链表。例如，有一个n项的链表，该表的第一项指向大小为4KB的空闲区链表表头的指针，第二项是指向大小为8KB的空闲区链表表头的指针，第三项是指向大小为12KB的空闲区链表表头的指针，以此类推。像21KB这样的空闲区即可以放在20KB的链表中也可以放在一个专门存放大小比较特别的空闲区的链表中。</p>
<p>快速适配算法寻找一个指定大小的空闲区是十分快速的，但它和所有将空闲区按大小排序的方案一样都有一个共同的缺点，即在一个进程终止或被换出时，寻找它的邻块，查看是否可以合并的过程是非常耗时的。如果不进行合并，内存将会很快分裂出大量的进程无法利用的小空闲区。</p>
<h3 id="位图"><a href="#位图" class="headerlink" title="位图"></a>位图</h3><p>位图的核心思想是<strong>将整个堆划分为大量的块</strong>，<strong>每个块的大小相同</strong>。</p>
<p>当用户请求内存的时候，总是分配整数个块的空间给用户，第一个块我们称之为已分配区域的头，其余的称为已分配区域的主体。而我们可以使用一个整数数组来记录块的使用情况。由于每个块只有头&#x2F;主体&#x2F;空闲三种状态，因此仅仅需要两位即可表示一个块，因此称为位图。假设堆的大小为1MB，那么让一个块大小为128字节，那么总共就有1M&#x2F;128&#x3D;8k个块，可以用8k&#x2F;（32&#x2F;2）&#x3D;512个int来存储。这有512个int的数组就是一个位图，其中每两位代表一个块。当用户请求300字节的内存时，堆分配给用户3个块，并将相应的位图的相应位置标记为头或躯体。</p>
<p><img src="/2021/12/06/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E2%80%94%E5%A0%86%E5%92%8C%E6%A0%88/map.png"></p>
<p>这个堆分配了3片内存，分别有2&#x2F;4&#x2F;1个块，用虚线标出。其对应的位图将是：</p>
<p>(HIGH) 11 00 00 10 10 10 11 00 00 00 00 00 00 00 10 11 (LOW）</p>
<p>其中11表示H(头），10表示主体（Body），00表示空闲（Free）。</p>
<h3 id="对象池"><a href="#对象池" class="headerlink" title="对象池"></a>对象池</h3><p>以上介绍的堆管理方法是最为基本的两种，实际上在一些场合，被分配对象的大小是较为固定的几个值，这时候我们可以针对这样的特征设计一个更为高效的堆算法，称为对象池。<br>对象池的思路很简单，如果每一次分配的空间大小都一样，那么就可以按照这个每次请求分配的大小作为一个单位，把整个堆空间划分为大量的小块，每次请求的时候只需要找到一个小块就可以了。<br>对象池的管理方法可以采用空闲链表，也可以采用位图，与它们的区别仅仅在于它假定了每次请求的都是一个固定的大小，因此实现起来比较容易。由于每次总是只请求一个单位的内存，因此请求得到满足的速度非常快，无须查找一个足够大的空间。</p>
<h3 id="综合运用多种算法"><a href="#综合运用多种算法" class="headerlink" title="综合运用多种算法"></a>综合运用多种算法</h3><p>实际上很多实际应用中，<strong>堆的分配算法往往是采用多种算法复合而成</strong>。</p>
<p>比如对于glibc来说，</p>
<p>它对于小于64字节的空间申请时采用类似于对象池的方法；</p>
<p>对于大于512字节的空间申请采用的是最佳适配算法；</p>
<p>对于大于64字节而小于512字节的，它会根据情况采用上述方法中的折中策略；</p>
<p>对于大于128KB的申请，它会使用mmap机制直接向操作系统申请空间。                                                           </p>
<blockquote>
<p>参考链接</p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/vincently/p/4671739.html">堆和动态内存管理</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/06/%E5%89%91%E6%8C%87Offer%E9%A2%98%E8%A7%A3-2/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/06/%E5%89%91%E6%8C%87Offer%E9%A2%98%E8%A7%A3-2/" class="post-title-link" itemprop="url">剑指Offer题解-2</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-06 13:34:17" itemprop="dateCreated datePublished" datetime="2021-12-06T13:34:17+08:00">2021-12-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-08 01:50:19" itemprop="dateModified" datetime="2021-12-08T01:50:19+08:00">2021-12-08</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h4 id="剑指-Offer-44-数字序列中某一位的数字"><a href="#剑指-Offer-44-数字序列中某一位的数字" class="headerlink" title="剑指 Offer 44. 数字序列中某一位的数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/">剑指 Offer 44. 数字序列中某一位的数字</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">findNthDigit</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> d = <span class="number">1</span>, mul = <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 重要点，一个 d 位数有 9*10^(d - 1)个，那么就一共有 d* 9*10^(d - 1) 位</span></span><br><span class="line">        <span class="keyword">while</span>(n &gt; (<span class="type">long</span> <span class="type">long</span> )d * <span class="number">9</span> * mul) &#123;</span><br><span class="line">            n -= d * <span class="number">9</span> * mul; </span><br><span class="line">            ++d;</span><br><span class="line">            mul *= <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 0  1  2  3  4</span></span><br><span class="line">        <span class="comment">// 00 11 22 33 44</span></span><br><span class="line">        <span class="type">int</span> num = mul + (n - <span class="number">1</span>)/ d;  <span class="comment">// 索引要 - 1</span></span><br><span class="line">        <span class="type">int</span> pos = (n - <span class="number">1</span>)% d;</span><br><span class="line">        string s = <span class="built_in">to_string</span>(num);</span><br><span class="line">        <span class="keyword">return</span> s[pos] - <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-33-二叉搜索树的后序遍历序列"><a href="#剑指-Offer-33-二叉搜索树的后序遍历序列" class="headerlink" title="剑指 Offer 33. 二叉搜索树的后序遍历序列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/">剑指 Offer 33. 二叉搜索树的后序遍历序列</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">bool</span> ans = <span class="literal">true</span>;</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">verifyPostorder</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; postorder)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">help</span>(postorder, <span class="number">0</span>, postorder.<span class="built_in">size</span>() - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">help</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; v, <span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l &gt; r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> root = v[r];</span><br><span class="line">        <span class="type">int</span> pos = l;</span><br><span class="line">        <span class="keyword">while</span>(pos &lt; r &amp;&amp; v[pos] &lt; root) ++pos;</span><br><span class="line">        <span class="type">int</span> idx = pos;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = idx ; i &lt;= r - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(v[i] &lt; root) &#123;</span><br><span class="line">                ans = <span class="literal">false</span>;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">help</span>(v, l, idx - <span class="number">1</span>);</span><br><span class="line">        <span class="built_in">help</span>(v, idx, r - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-34-二叉树中和为某一值的路径"><a href="#剑指-Offer-34-二叉树中和为某一值的路径" class="headerlink" title="剑指 Offer 34. 二叉树中和为某一值的路径"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/">剑指 Offer 34. 二叉树中和为某一值的路径</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:     </span><br><span class="line"></span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; ans;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; temp;</span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">pathSum</span>(TreeNode* root, <span class="type">int</span> targetSum) &#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span> ans;</span><br><span class="line">        <span class="built_in">dfs</span>(root,targetSum);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(TreeNode* root,<span class="type">int</span> sum)</span></span>&#123;</span><br><span class="line">        temp.<span class="built_in">push_back</span>(root-&gt;val);</span><br><span class="line">        <span class="type">int</span> s = sum - root-&gt;val;</span><br><span class="line">        <span class="keyword">if</span>(!root-&gt;left &amp;&amp; !root-&gt;right &amp;&amp; s == <span class="number">0</span>) &#123;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(temp);</span><br><span class="line">            temp.<span class="built_in">pop_back</span>(); <span class="comment">// 回溯</span></span><br><span class="line">            <span class="keyword">return</span>;            </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(root-&gt;left) <span class="built_in">dfs</span>(root-&gt;left, s);</span><br><span class="line">        <span class="keyword">if</span>(root-&gt;right) <span class="built_in">dfs</span>(root-&gt;right, s);</span><br><span class="line">        temp.<span class="built_in">pop_back</span>(); <span class="comment">// 回溯</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-57-和为s的两个数字"><a href="#剑指-Offer-57-和为s的两个数字" class="headerlink" title="剑指 Offer 57. 和为s的两个数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/he-wei-sde-liang-ge-shu-zi-lcof/">剑指 Offer 57. 和为s的两个数字</a></h4><p>双指针</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">twoSum</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> target)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> l = <span class="number">0</span>, r = nums.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> sum = nums[l] + nums[r];</span><br><span class="line">            <span class="keyword">if</span>(sum == target) &#123;</span><br><span class="line">                <span class="keyword">return</span> &#123;nums[l], nums[r]&#125;;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span>(sum &lt; target) &#123;</span><br><span class="line">                l++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                r--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> &#123;&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-56-I-数组中数字出现的次数"><a href="#剑指-Offer-56-I-数组中数字出现的次数" class="headerlink" title="剑指 Offer 56 - I. 数组中数字出现的次数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/">剑指 Offer 56 - I. 数组中数字出现的次数</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">singleNumbers</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x : nums) &#123;</span><br><span class="line">            res ^= x;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 任意挑一个不为1的位，以此为依据分成两组</span></span><br><span class="line">        <span class="type">int</span> bit = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>((bit &amp; res) == <span class="number">0</span>) &#123;</span><br><span class="line">            bit &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> a = <span class="number">0</span>, b = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x : nums) &#123;</span><br><span class="line">            <span class="comment">// 这个 bit 就能把数字区分开成两组</span></span><br><span class="line">            <span class="keyword">if</span>(x &amp; bit) &#123;</span><br><span class="line">                a ^= x;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                b ^= x;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> &#123;a, b&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-56-II-数组中数字出现的次数-II"><a href="#剑指-Offer-56-II-数组中数字出现的次数-II" class="headerlink" title="剑指 Offer 56 - II. 数组中数字出现的次数 II"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/">剑指 Offer 56 - II. 数组中数字出现的次数 II</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">singleNumber</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> cnt[<span class="number">32</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        <span class="comment">// 对每一个数都统计一下每一个bit</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x : nums) &#123;</span><br><span class="line">            <span class="comment">// 从低位到高位依次提取</span></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">32</span>; i++) &#123;</span><br><span class="line">                cnt[i] += (x &gt;&gt; i) &amp; <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 从低位到高位</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">32</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(cnt[i] % <span class="number">3</span>) &#123;</span><br><span class="line">                ans |= <span class="number">1</span> &lt;&lt; i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-51-数组中的逆序对"><a href="#剑指-Offer-51-数组中的逆序对" class="headerlink" title="剑指 Offer 51. 数组中的逆序对"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/">剑指 Offer 51. 数组中的逆序对</a></h4><p>unfinished（最简洁的写法）</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; b;</span><br><span class="line">    <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">reversePairs</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        b.<span class="built_in">resize</span>(nums.<span class="built_in">size</span>());</span><br><span class="line">        <span class="built_in">help</span>(nums, <span class="number">0</span>, nums.<span class="built_in">size</span>() - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">help</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l &gt;= r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> m = (l + r)/<span class="number">2</span>;</span><br><span class="line">        <span class="built_in">help</span>(nums, l, m);</span><br><span class="line">        <span class="built_in">help</span>(nums, m + <span class="number">1</span>, r);</span><br><span class="line">        <span class="type">int</span> i = l, j = m + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k = l; k &lt;= r; k++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(j &gt; r || i &lt;= m &amp;&amp; nums[i] &lt;= nums[j]) b[k] = nums[i++];</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                ans += m - i + <span class="number">1</span>;</span><br><span class="line">                b[k] = nums[j++];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k = l; k &lt;= r; k++) &#123;</span><br><span class="line">            nums[k] = b[k];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-45-把数组排成最小的数"><a href="#剑指-Offer-45-把数组排成最小的数" class="headerlink" title="剑指 Offer 45. 把数组排成最小的数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/">剑指 Offer 45. 把数组排成最小的数</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">bool</span> <span class="title">cmp</span><span class="params">(<span class="type">const</span> string&amp; s1,<span class="type">const</span> string &amp;s2)</span></span>&#123;</span><br><span class="line">        string s12 = s1+s2;</span><br><span class="line">        string s21 = s2+s1;</span><br><span class="line">        <span class="keyword">return</span> s12 &lt; s21;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">string <span class="title">minNumber</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        string ans = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        vector&lt;string&gt; ss;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x:nums)&#123;</span><br><span class="line">            ss.<span class="built_in">push_back</span>(<span class="built_in">to_string</span>(x));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">sort</span>(ss.<span class="built_in">begin</span>(), ss.<span class="built_in">end</span>(),</span><br><span class="line">            [](string&amp;s1, string &amp;s2) &#123; <span class="keyword">return</span> s1 + s2 &lt; s2 + s1; &#125;</span><br><span class="line">        );</span><br><span class="line">        <span class="keyword">for</span>(string&amp; s:ss)&#123;</span><br><span class="line">            ans += s;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-57-II-和为s的连续正数序列"><a href="#剑指-Offer-57-II-和为s的连续正数序列" class="headerlink" title="剑指 Offer 57 - II. 和为s的连续正数序列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/">剑指 Offer 57 - II. 和为s的连续正数序列</a></h4><p>滑动窗口</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">findContinuousSequence</span>(<span class="type">int</span> target) &#123;</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt; &gt; ans;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> l = <span class="number">1</span>, r = <span class="number">2</span>; l &lt; r;) &#123;</span><br><span class="line">            <span class="type">int</span> sum = (l + r) * (r - l + <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span>(sum == target) &#123;</span><br><span class="line">                vector&lt;<span class="type">int</span>&gt; a;</span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> i = l; i &lt;= r; i++) &#123;</span><br><span class="line">                    a.<span class="built_in">push_back</span>(i);</span><br><span class="line">                &#125;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(a);</span><br><span class="line">                ++l; <span class="comment">// l移动</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span>(sum &lt; target) &#123;</span><br><span class="line">                ++r;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                ++l;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-47-礼物的最大价值"><a href="#剑指-Offer-47-礼物的最大价值" class="headerlink" title="剑指 Offer 47. 礼物的最大价值"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/">剑指 Offer 47. 礼物的最大价值</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxValue</span><span class="params">(vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&amp; g)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> m = g.<span class="built_in">size</span>(), n = g[<span class="number">0</span>].<span class="built_in">size</span>();</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt; &gt; <span class="built_in">f</span>(m , <span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(n));</span><br><span class="line">        <span class="comment">// f[0][0] = grid[0][0];</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">                <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">                res = <span class="built_in">max</span>(res, g[i][j] + (i ? f[i - <span class="number">1</span>][j] : <span class="number">0</span>));</span><br><span class="line">                res = <span class="built_in">max</span>(res, g[i][j] + (j ? f[i][j - <span class="number">1</span>] : <span class="number">0</span>));</span><br><span class="line">                f[i][j] = res;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> f[m - <span class="number">1</span>][n - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>









<h4 id="剑指-Offer-46-把数字翻译成字符串"><a href="#剑指-Offer-46-把数字翻译成字符串" class="headerlink" title="剑指 Offer 46. 把数字翻译成字符串"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/">剑指 Offer 46. 把数字翻译成字符串</a></h4><p>记忆化搜索</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    map&lt;<span class="type">int</span>, <span class="type">int</span>&gt; mp;</span><br><span class="line">    <span class="type">int</span> len;</span><br><span class="line">    string s;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">translateNum</span><span class="params">(<span class="type">int</span> num)</span> </span>&#123;</span><br><span class="line">        s = <span class="built_in">to_string</span>(num);</span><br><span class="line">        len = s.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">dfs</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> i)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(i == len) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(mp.<span class="built_in">count</span>(i)) &#123;</span><br><span class="line">            <span class="keyword">return</span> mp[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(i + <span class="number">1</span> &lt;= len) &#123;</span><br><span class="line">            res += <span class="built_in">dfs</span>(i + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(i + <span class="number">2</span> &lt;= len) &#123;</span><br><span class="line">            <span class="type">bool</span> f = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">if</span>(s[i] == <span class="string">&#x27;0&#x27;</span>) f = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">if</span>((s[i] - <span class="string">&#x27;0&#x27;</span>) * <span class="number">10</span> + s[i + <span class="number">1</span>] - <span class="string">&#x27;0&#x27;</span> &gt; <span class="number">25</span>) f = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">if</span>(f) &#123;</span><br><span class="line">                res += <span class="built_in">dfs</span>(i + <span class="number">2</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        mp[i] = res;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">translateNum</span><span class="params">(<span class="type">int</span> num)</span> </span>&#123;</span><br><span class="line">        string s = <span class="built_in">to_string</span>(num);</span><br><span class="line">        <span class="type">int</span> m = s.<span class="built_in">size</span>();</span><br><span class="line">        s = <span class="string">&quot; &quot;</span> + s;</span><br><span class="line">        <span class="type">int</span> f[<span class="number">40</span>] = &#123;<span class="number">1</span>&#125;; <span class="comment">// 空串的分割种数为 1</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">1</span>; i &lt;= m; i++) &#123;</span><br><span class="line">            f[i] = f[i - <span class="number">1</span>];</span><br><span class="line">            <span class="keyword">if</span>(i &gt;= <span class="number">2</span>) &#123;</span><br><span class="line">                <span class="type">int</span> x = <span class="built_in">stoi</span>(s.<span class="built_in">substr</span>(i - <span class="number">1</span>, <span class="number">2</span>));</span><br><span class="line">                <span class="keyword">if</span>(x &gt;= <span class="number">10</span> &amp;&amp; x &lt;= <span class="number">25</span>) f[i] += f[i - <span class="number">2</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> f[m];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-48-最长不含重复字符的子字符串"><a href="#剑指-Offer-48-最长不含重复字符的子字符串" class="headerlink" title="剑指 Offer 48. 最长不含重复字符的子字符串"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/">剑指 Offer 48. 最长不含重复字符的子字符串</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">lengthOfLongestSubstring</span><span class="params">(string s)</span> </span>&#123;</span><br><span class="line">        set&lt;<span class="type">char</span>&gt; st;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> l = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> r = <span class="number">0</span>; r &lt; s.<span class="built_in">size</span>(); r++) &#123;</span><br><span class="line">            <span class="keyword">while</span>(l &lt; r &amp;&amp; st.<span class="built_in">count</span>(s[r])) &#123;</span><br><span class="line">                st.<span class="built_in">erase</span>(s[l++]);</span><br><span class="line">            &#125;</span><br><span class="line">            st.<span class="built_in">insert</span>(s[r]);</span><br><span class="line">            ans = <span class="built_in">max</span>(ans, r - l + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-53-I-在排序数组中查找数字-I"><a href="#剑指-Offer-53-I-在排序数组中查找数字-I" class="headerlink" title="剑指 Offer 53 - I. 在排序数组中查找数字 I"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/">剑指 Offer 53 - I. 在排序数组中查找数字 I</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">search</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">auto</span> l = <span class="built_in">lower_bound</span>(nums.<span class="built_in">begin</span>(), nums.<span class="built_in">end</span>(), target);</span><br><span class="line">        <span class="keyword">if</span>(l == nums.<span class="built_in">end</span>() || *l != target) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">auto</span> r = <span class="built_in">upper_bound</span>(nums.<span class="built_in">begin</span>(), nums.<span class="built_in">end</span>(), target);</span><br><span class="line">        <span class="keyword">return</span> r - l;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-52-两个链表的第一个公共节点"><a href="#剑指-Offer-52-两个链表的第一个公共节点" class="headerlink" title="剑指 Offer 52. 两个链表的第一个公共节点"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/">剑指 Offer 52. 两个链表的第一个公共节点</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">ListNode *<span class="title">getIntersectionNode</span><span class="params">(ListNode *headA, ListNode *headB)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (headA == <span class="literal">nullptr</span> || headB == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ListNode *pA = headA, *pB = headB;</span><br><span class="line">        <span class="keyword">while</span> (pA != pB) &#123;</span><br><span class="line">            pA = pA == <span class="literal">nullptr</span> ? headB : pA-&gt;next;</span><br><span class="line">            pB = pB == <span class="literal">nullptr</span> ? headA : pB-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pA;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-53-II-0～n-1中缺失的数字"><a href="#剑指-Offer-53-II-0～n-1中缺失的数字" class="headerlink" title="剑指 Offer 53 - II. 0～n-1中缺失的数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/">剑指 Offer 53 - II. 0～n-1中缺失的数字</a></h4><p>二分</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">missingNumber</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> l = <span class="number">0</span> ,r = a.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 找到第一个错位的索引</span></span><br><span class="line">        <span class="keyword">while</span>(l &lt; r)&#123;</span><br><span class="line">            <span class="type">int</span> m = l + (r - l) /<span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span>(a[m] == m)&#123;</span><br><span class="line">                l = m + <span class="number">1</span>; </span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                r = m;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> a[l] != l ? l : a.<span class="built_in">size</span>(); </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-54-二叉搜索树的第k大节点"><a href="#剑指-Offer-54-二叉搜索树的第k大节点" class="headerlink" title="剑指 Offer 54. 二叉搜索树的第k大节点"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/">剑指 Offer 54. 二叉搜索树的第k大节点</a></h4><p>不要使用中序遍历拿出所有的</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">int</span> ans, k ;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">kthLargest</span><span class="params">(TreeNode* root, <span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;k = k;</span><br><span class="line">        <span class="built_in">dfs</span>(root);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root || k &lt;= <span class="number">0</span>) <span class="keyword">return</span>;</span><br><span class="line">        <span class="built_in">dfs</span>(root-&gt;right);</span><br><span class="line">        <span class="keyword">if</span>(--k == <span class="number">0</span>) &#123; <span class="comment">// 没访问一个节点， --k</span></span><br><span class="line">            ans = root-&gt;val;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">dfs</span>(root-&gt;left);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-49-丑数"><a href="#剑指-Offer-49-丑数" class="headerlink" title="剑指 Offer 49. 丑数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/chou-shu-lcof/">剑指 Offer 49. 丑数</a></h4><p>优先队列</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">nthUglyNumber</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        set&lt;<span class="type">long</span>&gt; vis;</span><br><span class="line">        priority_queue&lt;<span class="type">long</span>, vector&lt;<span class="type">long</span>&gt;, greater&lt;<span class="type">long</span>&gt;&gt; pq;</span><br><span class="line">        pq.<span class="built_in">push</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">int</span> a[] = &#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>&#125;;</span><br><span class="line">        <span class="type">long</span> ans = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(n--) &#123;</span><br><span class="line">            ans = pq.<span class="built_in">top</span>();</span><br><span class="line">            pq.<span class="built_in">pop</span>();</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> y : a) &#123;</span><br><span class="line">                <span class="type">long</span> x = ans * y;</span><br><span class="line">                <span class="keyword">if</span>(!vis.<span class="built_in">count</span>(x)) &#123;</span><br><span class="line">                    vis.<span class="built_in">insert</span>(x);</span><br><span class="line">                    pq.<span class="built_in">push</span>(x);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;   </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<p>动态规划</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">nthUglyNumber</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">dp</span><span class="params">(n + <span class="number">1</span>)</span></span>;</span><br><span class="line">        dp[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> p2 = <span class="number">1</span>, p3 = <span class="number">1</span>, p5 = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="type">int</span> num2 = dp[p2] * <span class="number">2</span>, num3 = dp[p3] * <span class="number">3</span>, num5 = dp[p5] * <span class="number">5</span>;</span><br><span class="line">            dp[i] = <span class="built_in">min</span>(<span class="built_in">min</span>(num2, num3), num5);</span><br><span class="line">            <span class="keyword">if</span> (dp[i] == num2) &#123;</span><br><span class="line">                p2++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (dp[i] == num3) &#123;   <span class="comment">// 注意是 if （去重）</span></span><br><span class="line">                p3++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (dp[i] == num5) &#123;</span><br><span class="line">                p5++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-59-II-队列的最大值"><a href="#剑指-Offer-59-II-队列的最大值" class="headerlink" title="剑指 Offer 59 - II. 队列的最大值"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof/">剑指 Offer 59 - II. 队列的最大值</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MaxQueue</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    queue&lt;<span class="type">int</span>&gt; q;</span><br><span class="line">    deque&lt;<span class="type">int</span>&gt; dq; <span class="comment">// 递减</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">MaxQueue</span>() &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">max_value</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(dq.<span class="built_in">empty</span>()) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">return</span> dq.<span class="built_in">front</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">push_back</span><span class="params">(<span class="type">int</span> value)</span> </span>&#123;</span><br><span class="line">        q.<span class="built_in">push</span>(value);</span><br><span class="line">        <span class="keyword">while</span>(dq.<span class="built_in">size</span>() &amp;&amp; dq.<span class="built_in">back</span>() &lt; value) &#123;</span><br><span class="line">            dq.<span class="built_in">pop_back</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        dq.<span class="built_in">push_back</span>(value);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">pop_front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(q.<span class="built_in">empty</span>()) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="type">int</span> x = q.<span class="built_in">front</span>(); </span><br><span class="line">        q.<span class="built_in">pop</span>();</span><br><span class="line">        <span class="keyword">if</span>(dq.<span class="built_in">front</span>() == x) dq.<span class="built_in">pop_front</span>();</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-59-I-滑动窗口的最大值"><a href="#剑指-Offer-59-I-滑动窗口的最大值" class="headerlink" title="剑指 Offer 59 - I. 滑动窗口的最大值"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/">剑指 Offer 59 - I. 滑动窗口的最大值</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    deque&lt;<span class="type">int</span>&gt; dq;</span><br><span class="line">    queue&lt;<span class="type">int</span>&gt; q;</span><br><span class="line">    <span class="type">int</span> K;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">maxSlidingWindow</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.<span class="built_in">empty</span>()) <span class="keyword">return</span> &#123;&#125;;</span><br><span class="line">        K = k;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        <span class="comment">// 初始</span></span><br><span class="line">        <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(; i &lt; k; i++) &#123;</span><br><span class="line">            <span class="built_in">add</span>(nums[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 结果集大小</span></span><br><span class="line">        <span class="type">int</span> sz = nums.<span class="built_in">size</span>() - k + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(sz--) &#123;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(dq.<span class="built_in">front</span>());</span><br><span class="line">            <span class="keyword">if</span>(i &lt; nums.<span class="built_in">size</span>()) <span class="built_in">add</span>(nums[i++]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123;</span><br><span class="line">        q.<span class="built_in">push</span>(x);</span><br><span class="line">        <span class="keyword">while</span>(dq.<span class="built_in">size</span>() &amp;&amp; dq.<span class="built_in">back</span>() &lt; x) &#123;</span><br><span class="line">            dq.<span class="built_in">pop_back</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        dq.<span class="built_in">push_back</span>(x);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(q.<span class="built_in">size</span>() == K + <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="type">int</span> x = q.<span class="built_in">front</span>();</span><br><span class="line">            q.<span class="built_in">pop</span>();</span><br><span class="line">            <span class="keyword">if</span>(x == dq.<span class="built_in">front</span>()) dq.<span class="built_in">pop_front</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-65-不用加减乘除做加法"><a href="#剑指-Offer-65-不用加减乘除做加法" class="headerlink" title="剑指 Offer 65. 不用加减乘除做加法"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/">剑指 Offer 65. 不用加减乘除做加法</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="type">int</span> <span class="title">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 理论知识</span></span><br><span class="line">        <span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> C = <span class="number">0</span>, S; <span class="comment">// C表示临时进位，S表示临时结果</span></span><br><span class="line">        <span class="type">int</span> X, Y;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">32</span>; ++i) &#123;</span><br><span class="line">            X = a &amp; <span class="number">1</span>;</span><br><span class="line">            Y = b &amp; <span class="number">1</span>;</span><br><span class="line">            S = C ^ X ^ Y;</span><br><span class="line">            C = (X&amp;Y) | ((X | Y) &amp; C); <span class="comment">// key point</span></span><br><span class="line">            sum = sum | (S &lt;&lt; i);</span><br><span class="line">            a &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">            b &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-60-n个骰子的点数"><a href="#剑指-Offer-60-n个骰子的点数" class="headerlink" title="剑指 Offer 60. n个骰子的点数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/">剑指 Offer 60. n个骰子的点数</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">double</span>&gt; <span class="title">dicesProbability</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="function">vector&lt;<span class="type">double</span>&gt; <span class="title">dp</span><span class="params">(<span class="number">6</span>, <span class="number">1.0</span> / <span class="number">6.0</span>)</span></span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="function">vector&lt;<span class="type">double</span>&gt; <span class="title">tmp</span><span class="params">(<span class="number">5</span> * i + <span class="number">1</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; dp.<span class="built_in">size</span>(); j++) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k &lt; <span class="number">6</span>; k++) &#123;</span><br><span class="line">                    tmp[j + k] += dp[j] / <span class="number">6.0</span>; <span class="comment">// 概率论的加法公式</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            dp = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-62-圆圈中最后剩下的数字"><a href="#剑指-Offer-62-圆圈中最后剩下的数字" class="headerlink" title="剑指 Offer 62. 圆圈中最后剩下的数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/">剑指 Offer 62. 圆圈中最后剩下的数字</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">lastRemaining</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> m)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> idx = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> len = <span class="number">2</span>; len &lt;= n; len++) &#123;</span><br><span class="line">            <span class="comment">// (m ---&gt; 0, m + idx ---&gt; idx) 模len意义下 </span></span><br><span class="line">            <span class="comment">// aka. old_idx = (new_idx + m) % len;</span></span><br><span class="line">            idx = (m + idx) % len;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> idx;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">0 1 2 3 4    2   3 </span></span><br><span class="line"><span class="comment">3 4 0 1      0   0      </span></span><br><span class="line"><span class="comment">1 3 4        4   1 </span></span><br><span class="line"><span class="comment">1 3          1   1        </span></span><br><span class="line"><span class="comment">3            -   0 </span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-55-II-平衡二叉树"><a href="#剑指-Offer-55-II-平衡二叉树" class="headerlink" title="剑指 Offer 55 - II. 平衡二叉树"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/">剑指 Offer 55 - II. 平衡二叉树</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">bool</span> ans = <span class="literal">true</span>;</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">isBalanced</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">len</span>(root);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">len</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> llen = <span class="built_in">len</span>(root-&gt;left);</span><br><span class="line">        <span class="type">int</span> rlen = <span class="built_in">len</span>(root-&gt;right);</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">abs</span>(rlen - llen) &gt; <span class="number">1</span>) &#123;</span><br><span class="line">            ans = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">max</span>(llen, rlen) + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-61-扑克牌中的顺子"><a href="#剑指-Offer-61-扑克牌中的顺子" class="headerlink" title="剑指 Offer 61. 扑克牌中的顺子"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/">剑指 Offer 61. 扑克牌中的顺子</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> boolean <span class="title">isStraight</span><span class="params">(<span class="type">int</span>[] nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> joker = <span class="number">0</span>;</span><br><span class="line">        Arrays.<span class="built_in">sort</span>(nums); <span class="comment">// 数组排序</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] == <span class="number">0</span>) joker++; <span class="comment">// 统计大小王数量</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span>(nums[i] == nums[i + <span class="number">1</span>]) <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 若有重复，提前返回 false</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums[<span class="number">4</span>] - nums[joker] &lt; <span class="number">5</span>; <span class="comment">// 最大牌 - 最小牌 &lt; 5 则可构成顺子</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-68-II-二叉树的最近公共祖先"><a href="#剑指-Offer-68-II-二叉树的最近公共祖先" class="headerlink" title="剑指 Offer 68 - II. 二叉树的最近公共祖先"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/">剑指 Offer 68 - II. 二叉树的最近公共祖先</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">lowestCommonAncestor</span><span class="params">(TreeNode* root, TreeNode* p, TreeNode* q)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root || root == p || root == q)&#123;</span><br><span class="line">            <span class="keyword">return</span> root;</span><br><span class="line">        &#125;</span><br><span class="line">        TreeNode* lc = <span class="built_in">lowestCommonAncestor</span>(root-&gt;left, p, q);</span><br><span class="line">        TreeNode* rc = <span class="built_in">lowestCommonAncestor</span>(root-&gt;right, p, q);</span><br><span class="line">        <span class="keyword">if</span>(!lc) <span class="keyword">return</span> rc; <span class="comment">//左边这棵树既没有p，也没有q，看右边即可</span></span><br><span class="line">        <span class="keyword">if</span>(!rc) <span class="keyword">return</span> lc; <span class="comment">//右边这棵树既没有p，也没有q，看左边即可</span></span><br><span class="line">        <span class="comment">// 如果左边一个、右边一个，root就是lca。</span></span><br><span class="line">        <span class="keyword">return</span> root; </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-68-I-二叉搜索树的最近公共祖先"><a href="#剑指-Offer-68-I-二叉搜索树的最近公共祖先" class="headerlink" title="剑指 Offer 68 - I. 二叉搜索树的最近公共祖先"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/">剑指 Offer 68 - I. 二叉搜索树的最近公共祖先</a></h4><p>或者使用更一般的做法</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">lowestCommonAncestor</span><span class="params">(TreeNode* root, TreeNode* p, TreeNode* q)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        <span class="type">int</span> val = root-&gt;val;</span><br><span class="line">        <span class="keyword">if</span>(p-&gt;val &lt; val &amp;&amp; q-&gt;val &lt; val) <span class="keyword">return</span> <span class="built_in">lowestCommonAncestor</span>(root-&gt;left, p, q);</span><br><span class="line">        <span class="keyword">if</span>(p-&gt;val &gt; val &amp;&amp; q-&gt;val &gt; val) <span class="keyword">return</span> <span class="built_in">lowestCommonAncestor</span>(root-&gt;right, p, q);</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>






      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/15/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/15/">15</a><span class="page-number current">16</span><a class="page-number" href="/page/17/">17</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/17/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
